zookeeper简介:ZooKeeper致力于提供一个高性能、高可用,且具备严格的顺序访问控制能力的分布式协调服务,是雅虎公司创建,是Google的Chubby一个开源的实现,也是Hadoop和Hbase的重要组件。
zookeeper作为款高性能,高可用的工具,它可以实现多种多样的功能,如负载均衡,master选举,集群管理,配置管理,分布式锁等等。
今天我们就来针对zookeeper工具结合着springboot来实现服务的注册与发现的案例,类似于eureka。dubbo中的服务注册就是用zookeeper实现的。
要实现服务的注册和发现,我们必须要构建多个服务,这里就订单和产品为例。创建两个服务。
1、打开idea创建两个maven module ,order 和 product
2、在父maven的pom文件中引入 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>
<groupId>com.nginx.demo</groupId>
<artifactId>nginx_instance</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>order</module>
<module>product</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.12</version>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
3、产品模块,添加application.properties文件
4、在产品模块中新建Register
public class Register {
private static final String SERVER_PATH = "/product";
private static final String ZK_ADDRESS = "192.168.0.101:2181";
private static final int ZK_TIMEOUT = 20000;
/**
* 注册
* @param address 地址
*/
public void register(String address){
try {
ZooKeeper zooKeeper = new ZooKeeper(ZK_ADDRESS, ZK_TIMEOUT, WatchEvent -> {
});
Stat stat = zooKeeper.exists(SERVER_PATH, false);
if (stat == null) {
zooKeeper.create(SERVER_PATH, "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
}
String path = address;
//创建短暂的可排序的子节点
zooKeeper.create(SERVER_PATH+"/instance", path.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
}catch (Exception e){
e.printStackTrace();
}
}
}
这个类,主要是将当前产品服务的域名端口保存到zookeeper节点中。如果zookeeper没有安装的话这里补充一下:
下载路径: https://zookeeper.apache.org/releases.html
解压后,打开conf文件夹,将zoo_sample.cfg文件复制一份重命名为zoo.cfg
然后点击bin目录下的zkServer.cmd 运行即可。zookeeper默认连接端口2181
5、产品模块,添加Application.class文件,springboot启动类
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
@RequestMapping(value = "/product/id",method = RequestMethod.GET)
public String get(HttpServletRequest request){
return "访问 product 服务端口:"+ request.getLocalPort();
}
@Bean
public Register register(){
Register register = new Register();
register.register(getAddressAndPort());
return register;
}
private String getAddressAndPort(){
try {
Properties properties = new Properties();
InputStream stream = Application.class.getClassLoader().getResourceAsStream("application.properties");
properties.load(stream);
Object port = properties.get("server.port");
return "127.0.0.1:" + port;
}catch (Exception e){
throw new RuntimeException();
}
}
}
6、在订单模块order,添加ZookListener.class
public class ZookListener {
private static final String SERVER_PATH = "/product";
private static final String ZK_ADDRESS = "192.168.0.101:2181";
private static final int ZK_TIMEOUT = 20000;
private ZooKeeper zooKeeper;
private List<String> paths = new LinkedList<>();
public void init(){
try {
zooKeeper = new ZooKeeper(ZK_ADDRESS, ZK_TIMEOUT, (WatchEvent) -> {
//监听该节点的变化,如果节点出现变化,则重新获取节点下的ip和端口
if(WatchEvent.getType() == Watcher.Event.EventType.NodeChildrenChanged &&
WatchEvent.getPath().equals(SERVER_PATH)){
getChilds();
}
});
getChilds();
}catch (Exception e){
e.printStackTrace();
}
}
private void getChilds(){
List<String> ips =new LinkedList<>();
try {
//添加监听
List<String> childs = this.zooKeeper.getChildren(SERVER_PATH, true);
for(String child : childs){
byte[] obj = zooKeeper.getData(SERVER_PATH+"/"+child,false,null);
String path = new String(obj,"utf-8");
ips.add(path);
}
this.paths = ips;
}catch (Exception e){
e.printStackTrace();
}
}
public String getPath(){
if(paths.isEmpty()){
return null;
}
//这里我们随机获取一个ip端口使用
int index = new Random().nextInt(paths.size());
return paths.get(index);
}
}
该类的主要功能:获取产品服务保存在zookeeper中的多个服务ip和端口,并且监听zookeeper节点的变化,获取后并以各种算法获取其中一个ip端口使用,这里我们使用随机获取其中一个。
7、 订单服务添加application.properties ,这里端口我们直接用8080
8、订单服务添加Application.class文件,springboot启动类
@SpringBootApplication
@RestController
public class Application {
@Resource
private RestTemplate restTemplate;
@Resource
private ZookListener listener;
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean
public ZookListener zookListener(){
ZookListener listener = new ZookListener();
listener.init();
return listener;
}
@RequestMapping(value = "/order/id",method = RequestMethod.GET)
public String get(){
//从zookeeper中获取调用的ip
String path = listener.getPath();
if(path == null){
return "对不起,产品暂时停止服务!";
}
return restTemplate.getForObject("http://"+listener.getPath()+"/product/id",String.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
到此为止,所有的代码编写完毕,下面我们来进行测试。
首先是服务的注册:这里是产品服务的注册,我们需要将产品服务中的application.properties中的port修改成几个不同的端口。
然后每个端口都启动起来。
启动之后我们会发现zookeeper中已经将我们设置的各个端口都保存起来了。
这是zookeeper提供的一个java客户端连接工具,这里可以看到我们针对产品服务,启动了4个不同端口的集群服务。
现在我们打开浏览器,在地址栏输入订单order 服务的controller测试接口
刷新一次:
如果我们将idea中4个产品服务关掉一个后,会发现,zookeeper中针对于这个产品服务的节点数据也会被自动删除掉。如下
查看zookeeper节点数据:
本身是4个服务,现在关闭了一个,zookeeper保存的暂时节点数据也会自动删除,当然这个删除并不会马上触发,大概在10秒钟左右。
以上。