简介

本文介绍在Spring Boot环境下,使用ZooKeeper来注册服务。在本地环境搭建了ZooKeeper的伪集群环境。

本文参考了《架构探险》轻量级微服务架构 这本书。

搭建ZooKeeper伪集群

下载并安装

在官网下载ZooKeeper相关包后,解压到/etc/zookeeper下,并复制3份(ZooKeeper构建集群时,官网建议部署奇数个节点)。

修改配置

将conf/zoo_sample.cfg重命名为zoo.cfg,并将节点1修改为如下配置,具体意思请百度,这里不详细展开,因为这里配置的伪集群,所以要求各端口都不一样。

注意:在路径/home/billjiang/zookeeper/zkServer1目录下需要建立一个myid文件,并在文件中写入内容1

节点1配置

# The number of milliseconds of each tick
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/billjiang/zookeeper/zkServer1
clientPort=2181
#cluster
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890

节点2配置

# The number of milliseconds of each tick
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/billjiang/zookeeper/zkServer2
clientPort=2182
#cluster
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890

节点3配置

# The number of milliseconds of each tick
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/billjiang/zookeeper/zkServer3
clientPort=2183
#cluster
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890

批量启动集群shell脚本

为了启动集群,可以一个一个启动,也可以编写shell脚本批量启动:

zookeeper_start.sh

    #!/bin/bash  SERVERS="zkServer1 zkServer2 zkServer3"  for SERVER in $SERVERS  do  echo "当前"$SERVER"正在启动...................."  #ssh root@$SERVER "source /etc/profile;/usr/apps/zookeeper-3.4.9/bin/zkServer.sh start"  sudo /etc/zookeeper/$SERVER/bin/zkServer.sh startecho $SERVER"启动结束--------------------------------------------"                                                                         done  

当然也可以编写批量停止的shell脚本。执行批量启动命令后,如下:

启动集群后,可使用bin/zkCli.sh命令查看ZooKeeper的节点数据。

以上完成了ZooKeeper伪集群的搭建。

服务注册实现

为了演示服务在ZooKeeper上的注册过程,本文这里启动了一个Maven项目zookeeper-learn,项目包含三个module

  • core 注册的核心逻辑
  • client 客户端服务1
  • client2 客户端服务2

其中client/client2项目都依赖了core项目。在它们的pom.xml配置了该依赖

<dependency><groupId>com.cnpc</groupId><artifactId>core</artifactId><version>0.0.1-SNAPSHOT</version>
</dependency>

core项目的核心代码

定义服务注册接口ServiceRegistry

package com.example.core;public interface ServiceRegistry {/*** 注册服务信息** @param serviceName    服务名称* @param serviceAddress 服务地址*/void register(String serviceName, String serviceAddress);}

服务注册实现ServiceRegistryImpl
该服务实现将连接ZooKeeper集群,创建节点,并把服务的调用地址作为节点的值存储在该节点上。

package com.example.core;import org.apache.zookeeper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.util.concurrent.CountDownLatch;@Component
public class ServiceRegistryImpl implements ServiceRegistry, Watcher {private static Logger logger = LoggerFactory.getLogger(ServiceRegistryImpl.class);private static CountDownLatch latch = new CountDownLatch(1);private ZooKeeper zk;private static final int SESSION_TIMEOUT = 5000;public ServiceRegistryImpl() {}public ServiceRegistryImpl(String zkServers) {try {zk = new ZooKeeper(zkServers, SESSION_TIMEOUT, this);latch.await();logger.debug("connected to zookeeper");} catch (Exception ex) {logger.error("create zookeeper client failure", ex);}}private static final String REGISTRY_PATH = "/registry";@Overridepublic void register(String serviceName, String serviceAddress) {try {String registryPath = REGISTRY_PATH;if (zk.exists(registryPath, false) == null) {zk.create(registryPath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);logger.debug("create registry node:{}", registryPath);}//创建服务节点(持久节点)String servicePath = registryPath + "/" + serviceName;if (zk.exists(servicePath, false) == null) {zk.create(servicePath, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);logger.debug("create service node:{}", servicePath);}//创建地址节点String addressPath = servicePath + "/address-";String addressNode = zk.create(addressPath, serviceAddress.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);logger.debug("create address node:{} => {}", addressNode, serviceAddress);} catch (Exception e) {logger.error("create node failure", e);}}@Overridepublic void process(WatchedEvent watchedEvent) {if (watchedEvent.getState() == Event.KeeperState.SyncConnected)latch.countDown();}
}

客户端注册服务

客户端在启动时,会将自身服务节点注册到ZooKeeper集群中。

application.properties配置

server.address=127.0.0.1
server.port=8080
registry.servers=127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183

服务注册配置RegistryConfig
这样客户端可以读取appliaction.properties的ZooKeeper配置

package com.example.client;import com.example.core.ServiceRegistry;
import com.example.core.ServiceRegistryImpl;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@ConfigurationProperties(prefix = "registry")
public class RegistryConfig {private String servers;@Beanpublic ServiceRegistry serviceRegistry() {return new ServiceRegistryImpl(servers);}public void setServers(String servers) {this.servers = servers;}
}

用来测试的服务接口:TestController

@RestController
public class TestController {@RequestMapping(name="HelloService",method = RequestMethod.GET,path = "/hello")public String hello(){return "Hello";}
}

在项目启动的时候会将注解中name属性有值的方法注册到ZooKeeper集群中,

package com.example.client;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.example.core.ServiceRegistry;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;import java.util.Map;@Component
public class WebListener implements ServletContextListener {@Value("${server.address}")private String serverAddress;@Value("${server.port}")private int serverPort;@Autowiredpublic ServiceRegistry serviceRegistry;@Overridepublic void contextInitialized(ServletContextEvent sce) {ServletContext servletContext=sce.getServletContext();ApplicationContext applicationContext= WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);RequestMappingHandlerMapping mapping=applicationContext.getBean(RequestMappingHandlerMapping.class);Map<RequestMappingInfo,HandlerMethod> infoMap=mapping.getHandlerMethods();for (RequestMappingInfo info : infoMap.keySet()) {String serviceName=info.getName();System.out.println("-----------------"+serviceName);if(serviceName!=null){//注册服务serviceRegistry.register(serviceName,String.format("%s:%d",serverAddress,serverPort));}}}@Overridepublic void contextDestroyed(ServletContextEvent sce) {}
}

同样在client2项目中,相同的代码,唯一的区别就是client2的application.properties的server.port=8082

同时启动两个client2项目后,通过bin/zkCli.sh命令,连接到任意的一台ZooKeeper节点(因为ZooKeeper几点之间数据会保持同步)。显示如下信息:

[zk: localhost:2181(CONNECTED) 18] ls /registry/HelloService
[address-0000000004, address-0000000003]

使用get查看子节点的值

get /registry/HelloService/address-0000000003
127.0.0.1:8080
cZxid = 0x100000026
ctime = Wed Aug 09 18:00:56 CST 2017
mZxid = 0x100000026
mtime = Wed Aug 09 18:00:56 CST 2017
pZxid = 0x100000026
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x15dc5782fe8000d
dataLength = 14
numChildren = 0
get /registry/HelloService/address-0000000004
127.0.0.1:8081
cZxid = 0x100000028
ctime = Wed Aug 09 18:03:05 CST 2017
mZxid = 0x100000028
mtime = Wed Aug 09 18:03:05 CST 2017
pZxid = 0x100000028
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x15dc5782fe8000e
dataLength = 14
numChildren = 0

当停掉一台客户端client后,再次使用ls 命令,只显示address-0000000004

以上代码完成了ZooKeeper的服务注册。

基于ZooKeeper的服务注册实现相关推荐

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

    差异 基于Nginx的服务提供和消费 基于zookeeper的服务注册和发现 zk的负载均衡是可以调控,nginx只是能调权重,其他需要可控的都需要自己写插件:但是nginx的吞吐量比zk大很多,可以 ...

  2. RPC Demo(二) 基于 Zookeeper 的服务发现

    RPC Demo(二) 基于 Zookeeper 的服务发现 简介     基于上篇的:RPC Demo(一) Netty RPC Demo 实现     第二部分来实现使用Zookeeper作为服务 ...

  3. Zookeeper实现服务注册发现

    服务注册中心 ​ 之所以需要访问注册和服务发现是因为分布式系统中,服务之间需要相互调用,但若每个服务自己维护一份依赖的服务信息的话,就显得很麻烦,且自身维护的数据无法保证其实时性,当依赖的服务信息发生 ...

  4. zookeeper实现服务注册与发现

    在分布式架构的系统中,系统经常被暴露为服务以供其他系统调用,这也是SOA或微服务架构常用的模式. 为了使服务之间能够互相通信,需要有一个协调系统来管理这些服务,以便这些服务能够互相找到对方,这就是服务 ...

  5. 基于Eureka实现服务注册中心

    一.序言 当项目由单体结构拆分成一个个的服务,服务之间由本地调用转变成远程调用的时候,我们可以看成是通过一个个的url去获取数据.调用方和被调用方需要知道彼此的状态才可互相通信,而注册中心正是屏蔽了他 ...

  6. zookeeper curator 服务注册

    https://fobject.iteye.com/blog/2294728 Zookeeper & Curator 服务注册  curator是最简单的Zookeeper客户端 Curato ...

  7. 一个故事,一段代码告诉你如何使用不同语言(GolangC#)提供相同的能力基于Consul做服务注册与发现

    文章目录 引言 什么是微服务 传统服务 微服务 什么是服务注册与服务发现 为什么要使用不同的语言提供相同的服务能力 服务协调器 服务注册 Golang C#(.NetCore3.1) 服务发现 通过H ...

  8. Spring Cloud 基于Consul 实现服务注册与发现

    Spring Cloud自己体系中的注册中心为Eureka,同时也支持其它服务来进行服务注册与发现.本文介绍使用Consul来实现服务注册与发现,并整合进Spring Cloud项目中进行使用. 本文 ...

  9. 基于ZooKeeper的Dubbo注册中心【转】

    2019独角兽企业重金招聘Python工程师标准>>> Zookeeper注册中心安装 建议使用dubbo-2.3.3以上版本的zookeeper注册中心客户端.Zookeeper是 ...

最新文章

  1. linux 内核 编译详解
  2. 二叉树的前中后序遍历之迭代法(统一风格迭代方式)
  3. Laravel框架性能优化
  4. java脚本含义_set -e在bash脚本中的含义是什么?
  5. bootstrap 模态窗口按钮位置_Bootstrap模态框(modal)垂直居中
  6. test title
  7. 聊聊 Java 面试的一些坑
  8. IT黑马成长之CSDN第一篇博客
  9. groovy语法基础
  10. Spring 注解@Value详解
  11. 【微信小程序】wx:for的使用
  12. Vue3入门到精通--reactive以及reactive相关函数
  13. python统计单词出现次数
  14. 使用百度地图时如何隐藏百度地图logo
  15. 利用腾讯 优图visionseed硬件 实现人脸疲劳检测项目(包括数据读取,数据保存,数据web端展示)
  16. DHT11温湿度传感器(详细)
  17. Social-STGCNN: A Social Spatio-Temporal GCNN for Human Trajectory Prediction(CVPR2020)论文阅读笔记
  18. 深富策略:资源股高位杀跌消费白马迎来反攻能否配置
  19. “打印机故障”,我的解决方案
  20. R语言与回归分析计算实例

热门文章

  1. python课后心得体会_PYTHON学习第四天课后总结:
  2. java之进阶语法(Object类及日期时间类)
  3. 【资料整理】01-学习资料之分门别类
  4. Linux——Linux驱动之基本理论常识总结(什么是Linux驱动?Linux驱动需要掌握哪些?ARM处理体系架构及前世今生)
  5. 爱彼迎2021年旅行洞察:“国内游”继续成为主题,旅行者渴望“旅居四方”
  6. CrossValidation十字交叉验证的Python实现
  7. go int 转成time.Duration int64转成time.Duration
  8. 常用生物信息学数据库简介
  9. 文件系统调用和Linux文件系统基础
  10. 格子游戏(并查集)——信奥一本通