Zookeeper实现服务注册/发现
what that?
Zookeeper在分布式开发中使用频繁,但许多框架都对其进行了封装,初学者可能无法较好的理解其工作原理,该文章演示了使用Zookeeper实现服务注册,服务发现的简单demo,希望能达到抛砖引玉的效果;
why need RegisterCenter?
之所以需要访问注册和服务发现是因为分布式系统中,服务之间需要相互调用,但若每个服务自己维护一份依赖的服务信息的话,就显得很麻烦,且自身维护的数据无法保证其实时性,当依赖的服务信息发生变更时,无法及时获取更新,解决方案就是引入一个注册中心,服务提供方将自己的信息写入到注册中心,服务使用方从注册中心来获取服务信息; 如下图:
client表示服务使用方,server表示服务提供方

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

实现
首先保证Zookeeper以安装启动,且可以正常访问
创建Maven项目并添加Zookeeper的Java客户端依赖(注意版本号需>3.6)
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.6.1</version>
</dependency>
编写服务提供方
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;
}
}
}
编写服务服务使用方
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();
}
}
}
打包服务端代码,该步骤可忽略,仅为了测试客户端正确性, 为了在打包时附带其全部依赖,此处借助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包
将jar上传至虚拟机并运行
java -jar ZookeeperTest-1.0-SNAPSHOT.jar
若没有其他问题则客户端依然可以正常连接服务器发送消息;
以上便是使用Zookeeper实现服务注册和服务发现的具体步骤,在实际开发中,我们可能还会将提供的服务部署为集群,这时可将集群中的各个服务信息作为子节点注册到指定节点下,客户端监听该节点变化,获取子节点列表从而获取到服务列表,还可以在此基础上加上负载均衡算法实现对服务列表的合理访问; 如图:

Zookeeper实现服务注册/发现的更多相关文章
- 分布式应用开发 | SpringBoot+dubbo+zookeeper实现服务注册发现 | 远程服务调用
前言 通过新建两个独立服务--提供者.消费者,模拟两个独立分布的应用,通过使用dubbo+zookeeper来实现远程服务调用. 目录 项目搭建 provider-server consumer-se ...
- Web Api 基于Zookeeper的服务注册与发现
安装与差异 Zookeeper安装请参考我上篇文章 http://www.cnblogs.com/woxpp/p/7700368.html 基于Nginx的服务提供和消费 基于zookeeper的服务 ...
- 【转帖】基于Zookeeper的服务注册与发现
http://www.techweb.com.cn/network/hardware/2015-12-25/2246973.shtml 背景 大多数系统都是从一个单一系统开始起步的,随着公司业务的快速 ...
- 服务注册发现与注册中心对比-Eureka,Consul,Zookeeper,Nacos对比
服务注册发现与注册中心对比-Eureka,Consul,Zookeeper,Nacos对比 注册中心简介 流程和原理 基础流程 核心功能 1.Eureka.Consul.Zookeeper三者异同点 ...
- python与consul 实现gRPC服务注册-发现
背景 通过对gRPC的介绍我们知道,当正常启动服务后,我们只需要知道ip,port就可以进行gRPC的连接.可以想到,这种方式并不适合用于线上环境,因为这样直连的话就失去了扩展性,当需要多机部署的时候 ...
- 服务注册发现、配置中心集一体的 Spring Cloud Consul
前面讲了 Eureka 和 Spring Cloud Config,今天介绍一个全能选手 「Consul」.它是 HashiCorp 公司推出,用于提供服务发现和服务配置的工具.用 go 语言开发,具 ...
- netty集群(一)-服务注册发现
上篇文章介绍了如何搭建一个单机版本的netty聊天室:https://www.jianshu.com/p/f786c70eeccc. 一.需要解决什么问题: 当连接数超过单机的极限时,需要将netty ...
- Consul 多数据中心下的服务注册发现与配置共享
1. Consul简介 Consul是HashiCorp公司推出的开源软件,它提供了一套分布式高可用可横向扩展的解决方案,能为微服务提供服务治理.健康检查.配置共享等能力. Eurake2.x ...
- spring-cloud-consul 服务注册发现与配置
下面是 Spring Cloud 支持的服务发现软件以及特性对比(Eureka 已停止更新,取而代之的是 Consul): Feature euerka Consul zookeeper etcd 服 ...
随机推荐
- php-fpm搜索php.ini很奇怪的一个现象
php-fpm 找不到 php.ini phpinfo 或者 php -i 上来看,搜索的目录都是/usr/local/php/etc下,但是事实上并没用去找这个目录 bin/php --ini 来看 ...
- vue组件试错
[Vue warn]: Property or method "child1" is not defined on the instance but referenced duri ...
- Python的逻辑结构和函数
1.Python中的逻辑结构 ①顺序执行 ②选择执行: if...elif...else... 没有switch..case.. ③循环执行: for...in... while... 没有do..w ...
- PAT-1057 Stack (树状数组 + 二分查找)
1057. Stack Stack is one of the most fundamental data structures, which is based on the principle of ...
- UVA10480 Sabotage
题目链接:https://cn.vjudge.net/problem/UVA-10480 知识点: 最小割 题目大意: 求最小割并打印出最小割必须割掉的边. 解题思路: 在跑完 \(sap\) 后的残 ...
- Docker入门 安装Tomcat以及报404解决方案
时间:2020/1/18 17:34:09 浏览:24 来源:互联网 记录简单的在Docker 上安装Tomcat 首先我是在云服务器上(Centos系统)安装的Docker,我们需要在https:/ ...
- html文本超出加省略号
如果实现单行文本的溢出显示省略号同学们应该都知道用text-overflow:ellipsis属性来,当然还需要加宽度width属来兼容部分浏览. 实现方法: overflow: hidden; te ...
- 转 vue动画总结
使用过渡类名(有进入及出去,适合显示隐藏,需要配合v-if) .v-enter,//进入前 .v-leave-to {//离开后 只需要入场动画 可以把v-leave-to删掉 opacity: 0; ...
- Python中对文件的读写
读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘. 读写文件就是请求操作系统打开一个文件对象(通常称为文件描述符),然后,通过操作系 ...
- 如何利用CSS选择器抓取京东网商品信息
前几天小编分别利用Python正则表达式.BeautifulSoup.Xpath分别爬取了京东网商品信息,今天小编利用CSS选择器来为大家展示一下如何实现京东商品信息的精准匹配~~ CSS选择器 目前 ...