what that?

Zookeeper在分布式开发中使用频繁,但许多框架都对其进行了封装,初学者可能无法较好的理解其工作原理,该文章演示了使用Zookeeper实现服务注册,服务发现的简单demo,希望能达到抛砖引玉的效果;

why need RegisterCenter?

之所以需要访问注册和服务发现是因为分布式系统中,服务之间需要相互调用,但若每个服务自己维护一份依赖的服务信息的话,就显得很麻烦,且自身维护的数据无法保证其实时性,当依赖的服务信息发生变更时,无法及时获取更新,解决方案就是引入一个注册中心,服务提供方将自己的信息写入到注册中心,服务使用方从注册中心来获取服务信息; 如下图:

client表示服务使用方,server表示服务提供方

实现的效果: 客户端可自动发现服务信息,当服务状态发生变化时(上线,下线,更换地址),客户端可以及时响应变化,效果如下图:

效果演示

实现

  1. 首先保证Zookeeper以安装启动,且可以正常访问

  2. 创建Maven项目并添加Zookeeper的Java客户端依赖(注意版本号需>3.6)

    				<dependency>
    <groupId>org.apache.zookeeper</groupId>
    <artifactId>zookeeper</artifactId>
    <version>3.6.1</version>
    </dependency>
  3. 编写服务提供方

    package com.jerry;
    
    import org.apache.zookeeper.CreateMode;
    import org.apache.zookeeper.KeeperException;
    import org.apache.zookeeper.ZooDefs;
    import org.apache.zookeeper.ZooKeeper;
    import org.apache.zookeeper.data.ACL; import java.io.IOException;
    import java.io.InputStream;
    import java.net.*;
    import java.nio.charset.StandardCharsets;
    import java.util.ArrayList;
    import java.util.Enumeration; import static java.net.InetAddress.getLocalHost; public class UserService { public static void main(String[] args) throws IOException, InterruptedException, KeeperException {
    new UserService().serving();
    } public void serving() throws IOException, KeeperException, InterruptedException {
    //获取本机ip地址
    String ip = null;
    Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
    while (networkInterfaces.hasMoreElements()) {
    NetworkInterface ni = (NetworkInterface) networkInterfaces.nextElement();
    Enumeration<InetAddress> nias = ni.getInetAddresses();
    while (nias.hasMoreElements()) {
    InetAddress ia = (InetAddress) nias.nextElement();
    if (!ia.isLinkLocalAddress() && !ia.isLoopbackAddress() && ia instanceof Inet4Address) {
    ip = ia.getHostAddress();
    }
    }
    }
    int port = 8988; //启动服务
    ServerSocket socket = new ServerSocket(port);
    System.out.println("服务器已启动...");
    //注册服务
    serverRegister(ip, port);
    //处理请求
    clientHandler(socket);
    } private void clientHandler(ServerSocket socket) throws IOException {
    while (true) {
    Socket accept = socket.accept();
    InputStream inputStream = accept.getInputStream();
    byte[] barr = new byte[1024];
    while (true) {
    int size = inputStream.read(barr);
    if (size == -1) {
    //System.out.println("客户端已关闭..");
    accept.close();
    break;
    }
    String s = new String(barr, 0, size);
    //输出客户端消息
    System.out.println(accept.getInetAddress().getHostAddress() + ": " + s);
    }
    } } private void serverRegister(String ip, int port) throws IOException, KeeperException, InterruptedException {
    //注册服务
    ZooKeeper zooKeeper = new ZooKeeper("10.211.55.4", 2181, null);
    try {
    ArrayList<ACL> acl = new ArrayList<>();
    acl.add(new ACL(31, ZooDefs.Ids.ANYONE_ID_UNSAFE));
    zooKeeper.create("/userServer", (ip + ":" + port).getBytes(StandardCharsets.UTF_8), acl, CreateMode.EPHEMERAL);
    System.out.println("服务发布成功!");
    } catch (KeeperException | InterruptedException e) {
    e.printStackTrace();
    throw e;
    }
    }
    }
  4. 编写服务服务使用方

    package com.yyh;
    
    import org.apache.zookeeper.*;
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.InetSocketAddress;
    import java.net.Socket;
    import java.util.Scanner; public class UserClient implements Watcher {
    String node = "/userServer"; //服务信息所在的节点 服务提供方和服务消费方一致
    private ZooKeeper zooKeeper;
    String server_ip;
    int server_port; public static void main(String[] args) throws Exception {
    //开始服务监听
    UserClient userClient = new UserClient();
    userClient.run();
    //当访问可用时与服务交互
    Scanner scanner = new Scanner(System.in);
    while (true){
    System.out.println("输入要发送的信息(e:退出)");
    String text = scanner.next();
    if (text.equals("e"))System.exit(-1);
    if (userClient.server_ip == null){
    System.err.println("没有可用的服务...");
    }else {
    userClient.sendToServer(text);
    }
    }
    } private void run() throws Exception {
    //连接zookeeper
    zooKeeper = new ZooKeeper("10.211.55.4:2181", 3000, null);
    //尝试获取服务信息
    getServerInfo();
    //添加对服务信息的永久监听
    zooKeeper.addWatch(node,this,AddWatchMode.PERSISTENT);
    } //获取服务信息
    private void getServerInfo() {
    try {
    byte[] data = zooKeeper.getData(node, false, null);
    String[] infos = new String(data).split(":");
    server_ip = infos[0];
    server_port = Integer.parseInt(infos[1]);
    System.out.println("获取服务信息成功!");
    System.out.println(server_ip+":"+ server_port);
    } catch (KeeperException e) {
    System.err.println("服务信息不存在! 等待服务上线........");
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    } //当节点状态发送变化时将执行该方法(通知处理)
    @Override
    public void process(WatchedEvent event) {
    if (event.getPath().equals(node)) {
    //根据具体逻辑处理不同的事件类型,此处只关心节点的创建删除和更新
    if (event.getType() == Event.EventType.NodeCreated) {
    System.err.println("服务上线了");
    getServerInfo();
    } else if (event.getType() == Event.EventType.NodeDataChanged) {
    System.err.println("服务更新了");
    getServerInfo();
    }else if (event.getType()== Event.EventType.NodeDeleted){
    server_ip = null;
    server_port = 0;
    System.err.println("服务下线了");
    }
    }
    } public void sendToServer(String text) {
    InetSocketAddress server_address = new InetSocketAddress(server_ip, server_port);
    Socket socket = new Socket();
    try {
    socket.connect(server_address);
    //System.out.println("连接服务器成功!");
    OutputStream outputStream = socket.getOutputStream();
    outputStream.write(text.getBytes());
    System.out.println("消息发送成功!");
    } catch (IOException e) {
    e.printStackTrace();
    }
    try {
    socket.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }
  5. 打包服务端代码,该步骤可忽略,仅为了测试客户端正确性, 为了在打包时附带其全部依赖,此处借助Spring的打包插件,在pom中添加以下内容:

    		<build>
    <plugins>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>1.5.6.RELEASE</version>
    <executions>
    <execution>
    <goals>
    <goal>repackage</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>

    注意:Spring-boot打包插件会自动获取项目中的主函数,必须保证主函数只有一个,所以需要暂时注释客户端的主函数,最后执行maven的package,得到jar包

  6. 将jar上传至虚拟机并运行

    java -jar ZookeeperTest-1.0-SNAPSHOT.jar

    若没有其他问题则客户端依然可以正常连接服务器发送消息;

以上便是使用Zookeeper实现服务注册和服务发现的具体步骤,在实际开发中,我们可能还会将提供的服务部署为集群,这时可将集群中的各个服务信息作为子节点注册到指定节点下,客户端监听该节点变化,获取子节点列表从而获取到服务列表,还可以在此基础上加上负载均衡算法实现对服务列表的合理访问; 如图:

Zookeeper实现服务注册/发现的更多相关文章

  1. 分布式应用开发 | SpringBoot+dubbo+zookeeper实现服务注册发现 | 远程服务调用

    前言 通过新建两个独立服务--提供者.消费者,模拟两个独立分布的应用,通过使用dubbo+zookeeper来实现远程服务调用. 目录 项目搭建 provider-server consumer-se ...

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

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

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

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

  4. 服务注册发现与注册中心对比-Eureka,Consul,Zookeeper,Nacos对比

    服务注册发现与注册中心对比-Eureka,Consul,Zookeeper,Nacos对比 注册中心简介 流程和原理 基础流程 核心功能 1.Eureka.Consul.Zookeeper三者异同点 ...

  5. python与consul 实现gRPC服务注册-发现

    背景 通过对gRPC的介绍我们知道,当正常启动服务后,我们只需要知道ip,port就可以进行gRPC的连接.可以想到,这种方式并不适合用于线上环境,因为这样直连的话就失去了扩展性,当需要多机部署的时候 ...

  6. 服务注册发现、配置中心集一体的 Spring Cloud Consul

    前面讲了 Eureka 和 Spring Cloud Config,今天介绍一个全能选手 「Consul」.它是 HashiCorp 公司推出,用于提供服务发现和服务配置的工具.用 go 语言开发,具 ...

  7. netty集群(一)-服务注册发现

    上篇文章介绍了如何搭建一个单机版本的netty聊天室:https://www.jianshu.com/p/f786c70eeccc. 一.需要解决什么问题: 当连接数超过单机的极限时,需要将netty ...

  8. Consul 多数据中心下的服务注册发现与配置共享

    1. Consul简介   Consul是HashiCorp公司推出的开源软件,它提供了一套分布式高可用可横向扩展的解决方案,能为微服务提供服务治理.健康检查.配置共享等能力.   Eurake2.x ...

  9. spring-cloud-consul 服务注册发现与配置

    下面是 Spring Cloud 支持的服务发现软件以及特性对比(Eureka 已停止更新,取而代之的是 Consul): Feature euerka Consul zookeeper etcd 服 ...

随机推荐

  1. PAT 1011 World Cup Betting (20分) 比较大小难度级别

    题目 With the 2010 FIFA World Cup running, football fans the world over were becoming increasingly exc ...

  2. MyBatis入门知识汇总

     为什么要使用MyBatis? 我们都知道,在学习mybatis之前,要在Java中操作数据库,需要用到JDBC,但是在使用JDBC时会有许多缺陷. 比如: 1.使用时需要先进行数据库连接,不用后要立 ...

  3. vue中事件代理

    由于在vue实例内部,this指向了实例本身,所以在编写事件代理的时候,用e.currentTarget引用绑定事件代理的元素,e.target引用事件目标元素.刚刚不注意想用this引用代理元素报错 ...

  4. 你 MySQL 中重复数据多吗,教你一招优雅的处理掉它们!

    在需要保证数据唯一性的场景中,个人觉得任何使用程序逻辑的重复校验都是不可靠的,这时只能在数据存储层做唯一性校验.MySQL 中以唯一键保证数据的唯一性,那么若新插入重复数据时,我们可以让 MySQL ...

  5. day08文件的操作(0221)

    #1.文件操作之追加数据01:f = open("yesterday01",'a+U',encoding="utf-8")#a= append,追加之意,w则为 ...

  6. ESlint中console.log报错问题

    ESlint中console.log报错问题 由于ESlint规范化,导致console.log的使用也会报错,下面是设置允许console.log控制台输出 描述:打开 package.json 文 ...

  7. CukeTest+Puppeteer的Web自动化测试(一)

    CukeTest+Puppeteer的Web自动化测试 一.初识BDD.Cucumber(黄瓜).CukeTest 行为驱动开发(Behavior Driven Development,BDD).行为 ...

  8. 王艳 201771010127《面向对象程序设计(java)》第十三周学习总结

    一:理论部分. 1.事件处理基础. 1)事件源:能够产生事件的对象都可以成为事件源,如文本框.按钮等.一个事件源是一个能够注册监听器并向监听器发送事件对象的对象. 2)事件监听器:事件监听器对象接收事 ...

  9. 王玉兰201771010128《面向对象程序设计(java)》第一周学习总结

    第一部分:课程准备部分 填写课程学习 平台注册账号: 平台名称 注册账号 博客园:www.cnblogs.com 夜空傅说 程序设计评测:https://pintia.cn/ 2326669056@q ...

  10. 检查可执行App类型是否为executable (腾讯上线预审核报错)otool工具使用

    https://blog.csdn.net/lovechris00/article/details/81561627 查看IPA文件的路径 1,解压缩 xcode导出的xxx.ipa文件 2,然后在解 ...