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. babel转码时generator的regeneratorRuntime

    今天写generator函数时发现出错:regeneratorRuntime. 在stackoverflow网友说需是本地babel软件包没有安装完全. package.json: "dev ...

  2. Python--WebDriverWait+expected_conditions的一个应用

    from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.s ...

  3. 敏捷为什么会失败之「PA-SA-WAKA-DA」理论

    在日常生活中,有种有趣的现象:我们更津津乐道于美好的故事,比如提到好莱坞,我们关注的只是大牌明星,却忽略了他们成名其背后的艰辛.对于那些成功的敏捷项目,也是如此.在我们见证成功的同时,却忘记了项目团队 ...

  4. 6.2 Go 匿名字段

    6.2 Go 匿名字段 Golang匿名字段:可以像访问字段成员那样,访问匿名字段方法,go编译器自动查找. package main import "fmt" type Stud ...

  5. linux常用命令---网络端口信息与进程管理

    进程管理 进程管理

  6. 如何利用BeautifulSoup选择器抓取京东网商品信息

    昨天小编利用Python正则表达式爬取了京东网商品信息,看过代码的小伙伴们基本上都坐不住了,辣么多的规则和辣么长的代码,悲伤辣么大,实在是受不鸟了.不过小伙伴们不用担心,今天小编利用美丽的汤来为大家演 ...

  7. 在 Linux 系统中如何管理 systemd 服务

    在上一篇文章<Linux的运行等级与目标>中,我介绍过 Linux 用 systemd 来取代 init 作为系统的初始化进程.尽管这一改变引来了很多争议,但大多数发行版,包括 RedHa ...

  8. Android_存储之scoped storage&媒体文件

    Scoped storage 文件存储介绍了内部存储和外部存储相关的内容.因为外部存储容易读写,所以在手机中经常看到很多“乱七八糟”的文件或文件夹,这些就是应用肆意创建的. Android Q(10) ...

  9. Car的旅行路线 luogu P1027 (Floyd玄学Bug有点毒瘤)

    luogu题目传送门! Car的旅行路线  问题描述 又到暑假了,住在城市A的Car想和朋友一起去城市B旅游.她知道每个城市都有四个飞机场,分别位于一个矩形的四个顶点上,同一个城市中两个机场之间有一 ...

  10. static关键字修饰属性

    static 静态的,可以修饰属性,方法,代码块(或初始化块) , 内部内 非static修饰的属性(实例变量):各个对象各自拥有一套各自的副本 static修饰属性(l类变量): 1.由类创建的所有 ...