Zookeeper应用之注册中心

前言

Zookeeper是可以实现注册中心相关功能的。

何为注册中心?

我们知道,随着系统业务扩张,为了满足业务规模要求,引入了分布式、微服务等相关技术。引入它们后,相关的进程通信变为了网络通信。进而出现了服务调用方和服务被调用方,由于被调用方提供的服务地址在分布式环境下不是唯一的,因而需要对它们进行统一管理,这个管理的模块称之为注册中心。

可以参照如下图。

upload successful

Provider代表服务提供方,由于有多个服务地址(分布式情形下,这里简化为一个),均需要向Registry模块注册自己的信息,Invoker代表服务调用方,它通过注册中心的通知或者主动订阅获得服务信息,进而发起请求。其中Registry模块即为注册中心。

注册中心作为服务框架核心模块,它是服务框架唯一核心链路上的一个集中点,所以它的好坏也影响着整个服务框架的可用性以及稳定性。

注册中心的话大部分公司都使用的开源实现,Dubbo体系中使用Zookeeper的居多,SpringCloud体系中使用Eureka的居多。

我们今天来看一下一些好的开源实现吧。

正文

我们使用Spring Initializr 初始化一个集成了Zookeeper注册中心的SpringBoot项目。

如图,选择Zookeeper作为注册中心及服务发现。

upload successful

完成后在pom文件里会有如下依赖。

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>

我们来分析下Spring团队是如何使用Zookeeper作为注册中心的。

找到这个jar包,目录结构如下图,可以清楚的明白。

discovery是用于服务发现的包。

serviceregistry是用于服务注册的包。

support包包含一些工具类等信息。

upload successful

先来看看serviceregistry包下的类。

upload successful

ServiceInstanceRegistration类是一个服务注册信息bean。
其他带AutoConfiguration的是结合properties文件进行自动配置的类,也不做过多介绍。
我们来看下ZookeeperServiceRegistry这个服务注册关键类。

它的主要方法如下:

upload successful

可以看到实现了register(服务注册),getServiceDiscovery(获取服务发现者),deregister(服务解绑)等方法。

继续查看两个关键方法服务注册和解绑。

upload successful

可以看到它是通过一个叫ServiceDiscoveryImpl类进行实现的。

upload successful

可以发现这个类在 curator-x-discovery包下。

upload successful

这个包就是对Zookeeper实现服务注册与发现的一套封装。

这个jar包可以看到internalRegisterService这个方法,如果在缓存里不存在此服务的话就会去创建一个,可以看到它的逻辑,最大重试三次创建节点,可以选择创建节点的类型(服务类型),如果出现异常要删除创建的节点。

upload successful

也可以看到这个类使用了ConcurrentMap作为数据缓存。

upload successful

这个类里面的start方法会注册所有服务。

upload successful

upload successful

而后最开始的ZookeeperServiceRegistry会调用此方法进而注册服务。

upload successful

再来看一下discovery(服务发现)包。

upload successful

这个包里面内容较多,我们只看下比较关键的类ZookeeperDiscoveryClient即可。

先看看它的getInstances方法,通过serviceId获取一个Service实例。

upload successful

在ServiceDiscoveryImpl中的方法如下:

可以看到会调用queryForInstances方法,通过name拿到Zookeeper的path,然后获取该path上的所有instancesIds,在通过name和id调用queryForInstances方法查询具体的实例信息。

upload successful

upload successful

我们创建一个hello项目,加入Web及Zookeeper Discovery 依赖后,进行必要配置,启动项目,便可以在Zookeeper里看到我们注册的服务信息。

upload successful

application.properties配置

1
2
3
4
5
spring.cloud.zookeeper.discovery.register=true
spring.cloud.zookeeper.discovery.root=/test
spring.cloud.zookeeper.connect-string=127.0.0.1:2181
spring.application.name=test
server.port=8081

HelloController内容

1
2
3
4
5
6
7
RestController
public class HelloController {
@RequestMapping("/hello")
public String hello(){
return "hello";
}
}

启用服务发现。

upload successful

启动项目后通过客户端连接到Zookeeper可以看到服务节点信息。

upload successful

PS:启动过程中如果出现错误,可能是Zookeeper客户端版本与服务端版本不一致,一般为客户端版本较高出现的问题,应当注意。

我们继续新建一个Test类,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RunWith(SpringRunner.class)
@SpringBootTest
public class HelloApplicationTest {
@Autowired
RestTemplate restTemplate;
@Autowired
DiscoveryClient client;
@Test
public void contextLoads() {
List<String> list=client.getServices();
for(String str:list){
ServiceInstance instance=client.getInstances(str).get(0);
String result=restTemplate.getForEntity(instance.getUri(),String.class).getBody();
System.out.println(result);
}
}
}

可以看到获得的ServiceInstance信息如下。

upload successful

使用restTemplate发送请求,获得结果。

使用RestTemplate时,应当在启动时声明这个Bean,如下。

1
2
3
4
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}

如果服务调用时出现异常,可能是host的问题,比如我上面的host为DELL-3020-PC,如果不在hosts文件里指定其解析为127.0.0.1,那么请求会出现异常,应当注意。

说到这里,其实Zookeeper作为注册中心的本质,就是把服务的一些信息(host,port,URI,serviceName等等)以顺序临时节点的形式存储在Zookeeper上,通过主动拉取或者通知的方式获取服务信息,服务调用者拿到信息后进行调用服务提供方。

因此我们也可以自己基于Zookeeper实现一个注册中心,但是根据上面我们可以知道,注册中心可以说是系统中的关键部分,如果出现问题,可能导致系统服务不可用,出现严重生产事故,因此,注册中心必须保证高可用性、高性能、实时性(如有服务出现问题,应该从注册中心剔除)等特点。

因此我们现在使用一些开源代码,如Erueka Discovery、Zookeeper Discovery等,毕竟这些代码已经经过了无数人的测验。

我们也应该多学习关于注册中心这一方面的知识,争取可以造一个属于自己的“轮子”。

总结

通过对 Spring Cloud 里 Zookeeper Discovery代码的部分分析,了解了注册中心的大致原理,明白了Zookeeper作为注册中心的原理,对于我们来说,也是蛮不错的一次学习过程。




-------------文章结束啦 ~\(≧▽≦)/~ 感谢您的阅读-------------

您的支持就是我创作的动力!

欢迎关注我的其它发布渠道