基于ZooKeeper实现简单的服务注册于发现
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Shaun_luotao/article/details/87098482
分布式系统离不开服务的注册与发现。这里我采用ZooKeeper实现一个简单的服务注册与发现的例子。
首先简单介绍一下Zookeeper的基本特性。
Zookeeper 实现了一个类似于文件系统的树状结构:
Zookeeper的数据结构
层次化的目录结构,命名符合常规文件系统规范。
每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识。
节点Znode可以包含数据和子节点(但是EPHEMERAL类型的节点不能有子节点)。
客户端应用可以在节点上设置监视器。
Zookeeper的节点类型
永久节点(除非手动删除,节点永远存在)
永久有序节点(按照创建顺序会为每个节点末尾带上一个序号如:root-1)
瞬时节点(创建客户端与 Zookeeper 保持连接时节点存在,断开时则删除并会有相应的通知)
瞬时有序节点(在瞬时节点的基础上加上了顺序)
思路:
既然Zookeeper能够在节点上保存一定的数据信息,那么我们在服务注册的时候,创建服务端的节点,并将服务的Ip地址和端口保存在Zookeeper的节点中,服务调用的时候,根据负载算法,获取到Zookeeper节点的节点数据,拿到IP地址和端口,根据IP地址和端口就能调用相应的服务。
创建三个新的SpringBoot工程,引入Zookeeper开发包。其中两个工程演示服务端,一个工程演示客户端。
服务端工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shaun.zookeeper</groupId>
<artifactId>serviceproject</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>serviceproject</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在启动类中开启使用 @ServletComponentScan注解后,直接通过@WebListener 开启监听,启动项目的时候调用ServiceRegister,将服务端的物理地址信息注册到Zookeeper。
启动类
@SpringBootApplication
@ComponentScan(basePackages = "com.shaun.*")
@ServletComponentScan
public class ServiceprojectApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceprojectApplication.class, args);
}
}
InitListener 监听器
@WebListener
public class InitListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent servletContextEvent){
Properties properties = new Properties();
try {
properties.load(InitListener.class.getClassLoader().getResourceAsStream("application.yml"));
String hostAddress = InetAddress.getLocalHost().getHostAddress();
String po = properties.getProperty("port");
int port = Integer.parseInt(po);
ServiceRegister.reister(hostAddress,port);
} catch (IOException e) {
e.printStackTrace();
}
}
}
ServiceRegister服务注册的具体实现。
public class ServiceRegister {
private static final String BASE_SERVICE = "/zookeeper";
private static final String SERVICE_NAME = "/server";
public static void reister(String address,int port){
String path = BASE_SERVICE+SERVICE_NAME;
try {
ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181",5000, null);
Stat exists = zooKeeper.exists(BASE_SERVICE+SERVICE_NAME,false);
if(exists == null){
zooKeeper.create(path,"".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
String server_path = address+":"+port;
zooKeeper.create(path+"/child",server_path.getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
System.out.println("产品服务注册成功!");
} catch (Exception e) {
e.printStackTrace();
}
}
}
为什么创建znode的时候是创建临时有序节点,因为服务关闭的时候,节点会自动删除。服务端开启节点监听会刷新服务列表。
新建一个测试controller,返回服务端的地址信息。方便测试客户端调用服务端。
@RestController
@RequestMapping("/product")
public class ProductController {
@RequestMapping("/getProduct")
public Map getProduct(@RequestBody Map entity){
Map map = new HashMap();
map.put("id",entity.get("id"));
map.put("name","你好");
return map;
}
}
客户端工程
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shaun.zookeeper</groupId>
<artifactId>clientproject</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>clientproject</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
和服务端类似,不过客户端在启动监听器中需获取Zookeeper中注册的服务列表。将服务列表保存到负载均衡类中。调用服务的时候,从负载均衡类中获取注册的服务端的物理地址信息。
启动监听程序
开启了节点监听,当节点事件类型watchedEvent为NodeChildrenChanged 并且节点路径和服务端注册的节点路径一致时,说明有服务关闭,更新获取到的服务节点。将服务节点信息保存到负载均衡类中。
@WebListener
public class InitListener implements ServletContextListener{
private static final String BASE_SERVICE = "/zookeeper";
private static final String SERVICE_NAME = "/server";
private ZooKeeper zooKeeper;
@Override
public void contextInitialized(ServletContextEvent servletContextEvent){
init();
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent){
}
public void init(){
try {
zooKeeper = new ZooKeeper("127.0.0.1:2181",5000, (watchedEvent -> {
if(watchedEvent.getType()== Watcher.Event.EventType.NodeChildrenChanged && watchedEvent.getPath().equals(BASE_SERVICE+SERVICE_NAME)){
updateServerList();
}
}));
updateServerList();
} catch (IOException e) {
e.printStackTrace();
}
}
public void updateServerList(){
List <String> newServiceList = new ArrayList<>();
try {
List <String> children = zooKeeper.getChildren(BASE_SERVICE+SERVICE_NAME,true);
for(String subNode:children){
byte [] data = zooKeeper.getData(BASE_SERVICE + SERVICE_NAME +"/" + subNode,false,null);
String host = new String(data,"utf-8");
System.out.println("host:"+host);
newServiceList.add(host);
}
LoadBalanse.SERVICE_LIST = newServiceList;
} catch (Exception e) {
e.printStackTrace();
}
}
}
public abstract class LoadBalanse {
public volatile static List<String> SERVICE_LIST;
public abstract String choseServiceHost();
}
public class RandomLoadBalance extends LoadBalanse {
@Override
public String choseServiceHost() {
String result = "";
if(!CollectionUtils.isEmpty(SERVICE_LIST)){
int index = new Random().nextInt(SERVICE_LIST.size());
result = SERVICE_LIST.get(index);
}
return result;
}
}
调用示例:
@RestController
@RequestMapping("/order")
public class TestController {
private RestTemplate restTemplate = new RestTemplate();
private LoadBalanse loadBalanse = new RandomLoadBalance();
@RequestMapping("/getOrder")
public Object getProduct(@RequestBody Map entity){
String host = loadBalanse.choseServiceHost();
Map res = restTemplate.postForObject("http://"+host+"/product/getProduct",entity,Map.class);
res.put("host",host);
return res;
}
@RequestMapping("/test")
public Map test(){
return null;
}
}
启动服务端之前,我们看一下node节点,只有永久节点/zookeeper/server
启动成功后,输出服务注册成功!
再查下一下znode节点。发现child节点创建成功,说明服务注册成功。
启动客户端工程,测试/order/getOrder
根据测试结果,客户端调用8092或者8091完全是随机的。
本文只简单演示基于Zookeeper做服务注册与发现,Zookeeper还有很多特性未深入研究,示例工程后续会传到GitHub。
————————————————
版权声明:本文为CSDN博主「Shaun_luotao」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Shaun_luotao/article/details/87098482
基于ZooKeeper实现简单的服务注册于发现的更多相关文章
- .NET Core微服务之基于Steeltoe使用Eureka实现服务注册与发现
Tip: 此篇已加入.NET Core微服务基础系列文章索引 => Steeltoe目录快速导航: 1. 基于Steeltoe使用Spring Cloud Eureka 2. 基于Steelt ...
- 基于 Consul 实现 MagicOnion(GRpc) 服务注册与发现
0.简介 0.1 什么是 Consul Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置. 这里所谓的服务,不仅仅包括常用的 Api 这些服务,也包括软件开发过程 ...
- 【转帖】基于Zookeeper的服务注册与发现
http://www.techweb.com.cn/network/hardware/2015-12-25/2246973.shtml 背景 大多数系统都是从一个单一系统开始起步的,随着公司业务的快速 ...
- SpringCloud系列——Eureka 服务注册与发现
前言 Eureka是一种基于REST(具像状态传输)的服务,主要用于AWS云中定位服务,以实现中间层服务器的负载平衡和故障转移.本文记录一个简单的服务注册与发现实例. GitHub地址:https:/ ...
- 024.微服务架构之服务注册与发现(kubernetes / SpringCloud)
微服务 微服务是一种架构模式,一种分布式的架构风格. 顾名思义,micro service,将一个庞大的单体应用拆分成若干个“微小”的服务,服务间通过进程通讯完成原本在单体应用中的调用. 其中必要的六 ...
- 简单RPC框架-基于Consul的服务注册与发现
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- Web Api 基于Zookeeper的服务注册与发现
安装与差异 Zookeeper安装请参考我上篇文章 http://www.cnblogs.com/woxpp/p/7700368.html 基于Nginx的服务提供和消费 基于zookeeper的服务 ...
- Zookeeper服务注册与发现原理浅析
了解Zookeeper的我们都知道,Zookeeper是一种分布式协调服务,在分布式应用中,主要用来实现分布式服务的注册与发现以及分布式锁,本文我们简单介绍一下Zookeeper是如何实现服务的注册与 ...
- springcloud之服务注册与发现(zookeeper注册中心)-Finchley.SR2版
新年第一篇博文,接着和大家分享springcloud相关内容:本次主要内容是使用cloud结合zookeeper作为注册中心来搭建服务调用,前面几篇文章有涉及到另外的eureka作为注册中心,有兴趣的 ...
随机推荐
- 分布式调度平台XXL-JOB源码分析-调度中心
架构图 上图是我们要进行源码分析的2.1版本的整体架构图.其分为两大块,调度中心和执行器,本文先分析调度中心,也就是xxl-job-admin这个包的代码. 关键bean 在application.p ...
- 【转】Http和Https下的cookie的写入问题
网站https:// 可以登陆, 但是切换到http的时候不能登陆. 原因是:https访问的时候,返回的cookie设置了secure=1, 切换成http访问的时候, 这个时候不能操作那个cook ...
- JDK源码那些事儿之LinkedTransferQueue
在JDK8的阻塞队列实现中还有两个未进行说明,今天继续对其中的一个阻塞队列LinkedTransferQueue进行源码分析,如果之前的队列分析已经让你对阻塞队列有了一定的了解,相信本文要讲解的Lin ...
- JavaScript和JQuery之战再续
之前写过关于JavaScript和Jquery的之间的比较,现在再看比较偏向于理论知识,还不是很理解.经过这一段时间的项目的锻炼,对JQuery有了新的认识. 原生JavaScript和jQuery的 ...
- python爬虫——爬取网页数据和解析数据
1.网络爬虫的基本概念 网络爬虫(又称网络蜘蛛,机器人),就是模拟客户端发送网络请求,接收请求响应,一种按照一定的规则,自动地抓取互联网信息的程序.只要浏览器能够做的事情,原则上,爬虫都能够做到. 2 ...
- ES的入门学习
ES的入门:ES的雇员文档的设计和实现功能 ES的存放中包括:索引,类型,文档,字段 PUT /megacorp/employee/1{{ "first_name" : " ...
- C# 3.0 扩展方法&接口
namespace ExtensionInterfaceMethod { class Program { static void Main(string[] args) { //使用接口变量来调用扩展 ...
- 洛谷 P1725 琪露诺 题解
P1725 琪露诺 题目描述 在幻想乡,琪露诺是以笨蛋闻名的冰之妖精. 某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来.但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸.于是 ...
- 11、spark内核架构剖析与宽窄依赖
一.内核剖析 1.内核模块 1.Application 2.spark-submit 3.Driver 4.SparkContext 5.Master 6.Worker 7.Executor 8.Jo ...
- 【一起来烧脑】一步学会HTML体系
[外链图片转存失败(img-zk4xNuy1-1563431241992)(https://upload-images.jianshu.io/upload_images/11158618-4e9cac ...