版权声明:本文为博主原创文章,遵循 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实现简单的服务注册于发现的更多相关文章

  1. .NET Core微服务之基于Steeltoe使用Eureka实现服务注册与发现

    Tip: 此篇已加入.NET Core微服务基础系列文章索引 =>  Steeltoe目录快速导航: 1. 基于Steeltoe使用Spring Cloud Eureka 2. 基于Steelt ...

  2. 基于 Consul 实现 MagicOnion(GRpc) 服务注册与发现

    0.简介 0.1 什么是 Consul Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置. 这里所谓的服务,不仅仅包括常用的 Api 这些服务,也包括软件开发过程 ...

  3. 【转帖】基于Zookeeper的服务注册与发现

    http://www.techweb.com.cn/network/hardware/2015-12-25/2246973.shtml 背景 大多数系统都是从一个单一系统开始起步的,随着公司业务的快速 ...

  4. SpringCloud系列——Eureka 服务注册与发现

    前言 Eureka是一种基于REST(具像状态传输)的服务,主要用于AWS云中定位服务,以实现中间层服务器的负载平衡和故障转移.本文记录一个简单的服务注册与发现实例. GitHub地址:https:/ ...

  5. 024.微服务架构之服务注册与发现(kubernetes / SpringCloud)

    微服务 微服务是一种架构模式,一种分布式的架构风格. 顾名思义,micro service,将一个庞大的单体应用拆分成若干个“微小”的服务,服务间通过进程通讯完成原本在单体应用中的调用. 其中必要的六 ...

  6. 简单RPC框架-基于Consul的服务注册与发现

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  7. Web Api 基于Zookeeper的服务注册与发现

    安装与差异 Zookeeper安装请参考我上篇文章 http://www.cnblogs.com/woxpp/p/7700368.html 基于Nginx的服务提供和消费 基于zookeeper的服务 ...

  8. Zookeeper服务注册与发现原理浅析

    了解Zookeeper的我们都知道,Zookeeper是一种分布式协调服务,在分布式应用中,主要用来实现分布式服务的注册与发现以及分布式锁,本文我们简单介绍一下Zookeeper是如何实现服务的注册与 ...

  9. springcloud之服务注册与发现(zookeeper注册中心)-Finchley.SR2版

    新年第一篇博文,接着和大家分享springcloud相关内容:本次主要内容是使用cloud结合zookeeper作为注册中心来搭建服务调用,前面几篇文章有涉及到另外的eureka作为注册中心,有兴趣的 ...

随机推荐

  1. 开发搭建环境之springboot配置logback日志管理

    首先书写logback-spring.xml文件为: <?xml version="1.0" encoding="UTF-8"?><confi ...

  2. 《你们都是魔鬼吗》团队作业Beta冲刺---第一天

    团队作业Beta冲刺 项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 作业链接地址 团队名称 你们都是魔鬼吗 作业学习目标 (1)掌握软件黑盒测试技术:(2)学会编制软件 ...

  3. VS无ADO.NET实体数据模型?

    今天做EF的小例子时,发现需要添加实体数据模型,但是不管怎么找在新建项中都找不到这个选项,这是怎么回事,于是就开始百度吧,有的说可能是VS安装时没有全选,也有的人说可能是重装VS时,没有将注册表清除, ...

  4. dt6.0之mip改造-img正则替换mip-img

    最近没事,打算把自己的小项目改造为mip,进行测试学习,想把资讯栏目:http://zhimo.yuanzhumuban.cc/news/.全部改造为mip.但是MIP改造一项是:图片标签的改造.而且 ...

  5. 2.spring的主要模块作用

    spring中大约包含20过个模块, 主要包括以下几部分: 1. Core Container Core Container(核心容器)包含Core,Beans,Context和Expression ...

  6. Python 的内置字符串方法(收藏专用)

    Python 的内置字符串方法(收藏专用) method 字符串 string python3.x  python 4.7k 次阅读  ·  读完需要 44 分钟 5 字符串处理是非常常用的技能,但 ...

  7. 直接获取任意对象的 $('.xx').css('x') 值都是0

    <!-- 任意对象,直接获取他们的 x , y 都是为0: $('#xxx').css('x','y'); --> <!DOCTYPE html> <html lang= ...

  8. 五.划分LVM逻辑卷

    作用:    1.整合分散的空间    2.空间可以进行扩大   零散空闲存储 ---- 整合的虚拟磁盘 ---- 虚拟的分区   由众多的物理卷(PV)组合成卷组(VG),从卷组中划分多个逻辑卷(L ...

  9. Jmeter批量调用web service,发送message到tibco JMS queue

    [转载]Jmeter使用CSV Data Set Config参数化数据不重复的多次循环执行https://blog.csdn.net/qq_35123018/article/details/7877 ...

  10. luogu 2312 解方程 乱搞+取模

    思路非常好想,但是你很难想到去用这个算法,因为这个几乎就是个乱搞~ 我们发现多项式中每一个系数都很大,但是 $m$ 却很小,即最多只用 $10^6$ 个整数需要验证. 我们知道,如果一个数等于 $0$ ...