版权声明:本文为博主原创文章,遵循 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. Range与面向对象的Kotlin

    在上一次https://www.cnblogs.com/webor2006/p/11186089.html中已经用到了范围相关的东东了,如下: 所以这次专门针对Koltin范围相关的东东再来细学一下, ...

  2. No.1.测试Synchronized加锁String字符串后的多线程同步状况

    测试目的描述 Synchronized关键字锁定String字符串可能会带来严重的后果, 尽量不要使用 synchronized(String a) 因为JVM中,因为字符串常量池具有缓冲功能! 接下 ...

  3. :Spring-06 -AOP [面向切面编程] -配置异常通知的两种方式--AspectJ 方式 -Schema-based 方式

    三.配置异常通知的步骤(AspectJ 方式) 1.只有当切点报异常才能触发异常通知 2.在spring 中有AspectJ 方式提供了异常通知的办法 3.实现步骤: 3.1新建类,在类写任意名称的方 ...

  4. java在win系统下的环境的搭建

    学习Java第一步是配置本地开发环境,学习最基本的桌面开发,下面以win7为例配置Java开发环境,安装JDK的时候会默认安装JRE,根据提示安装就可以了. 首先去官网下载适合系统版本的JDK,下载地 ...

  5. 【Beta】 Scrum meeting 3

    第三天 日期:2019/6/26 前言 组内进行第三次讨论,所有组员都到场,项目已经全部完成. 一.今天任务完成情况.成员贡献时间 组员 任务 贡献时间 徐浩杰 完成项目的全部测试,项目运行稳定 2h ...

  6. 关于axios请求携带cookie以及封装

    axios跨域携带cookie需要配置 axios跨域发送请求的时候默认不会带上cookie的 + withCredentials的情况下,后端要设置Access-Control-Allow-Orig ...

  7. LOJ129 Lyndon 分解

    Lyndon 分解 样例 样例输入 1 ababa 样例输出 1 2 4 5 样例输入 2 bbababaabaaabaaaab 样例输出 2 1 2 4 6 9 13 18 样例输入 3 azAZ0 ...

  8. attention speech recognition

    Attention:是一种权重向量或矩阵,其往往用在Encoder-Decoder架构中,其权重越大,表示的context对输出越重要.计算方式有很多变种,但是核心都是通过神经网络学习而得到对应的权重 ...

  9. epoll版http服务器

    epoll是事件通知方式接收数据,效率比轮询要高 代码: import socket import re import select def client_server(new_client,recv ...

  10. JSONObject例子

    说起JSON,大家就谈不上陌生了,因为对于数据传输语言,各位只认json,即使有XML语言,但是各位很少用吧.我也是,但是之前用过的json转换工具各种各样,我记忆中有过GSON(google).fa ...