这是小卷对分布式系统架构学习的第8篇文章,在写第2篇文章已经讲过服务发现了,现在就从组件工作原理入手,讲讲注册中心
以下是面试题:
某团面试官:你来说说怎么设计一个注册中心?
我:注册中心嘛,就要有服务发现的功能,服务启动后把自己的信息注册上去,然后消费端可以拉取生产者的信息嘛,然后就能调用了
某团面试官:那你说说分布式系统中注册中心怎么选型呢?Eureka、Zookpeer、Nacos、Etcd这些中间件,你实际用的时候怎么考虑的?
我:就用Nacos啊,公司里大家都用这个
面试官:…
面试官:我知道了,今天的面试就到这吧…
1.注册中心的功能
设计注册中心时,先来思考下面几个问题:
- 服务如何注册
- consumer如何知道provider
- 注册中心怎么做到高可用,服务发现怎么做的
要想实现一个服务注册中心,必须具备以下功能:
- 服务注册:provider在注册中心完成注册
- 服务注销:服务提供者需在注册中心完成服务下线
- 心跳检测:检查服务提供者的健康状态
- 服务查询:消费者获取服务列表消息
- 服务变更查询:服务发生变更时,消费者通过查询变更获取最新服务列表
- 服务高可用
2.Zookeeper工作原理
我们还是选择一个比较活跃的中间件来展开学习注册中心的工作原理。
Zookeeper的文档官网:Apache ZooKeeper
官网解释Zookeeper名字的由来是因为分布式系统就像个动物园(Zoo),目前国内Dubbo场景下大多选用Zookeeper作为注册中心。官方解释它使用一个具有层次结构的命名空间(类似文件系统),用斜杠(“/”)分隔路径元素,根节点是(“/”)表示,无父节点。通过znodes(ZooKeeper节点)存储数据,如果znode有子节点,则无法删除该znode。这些数据可以被多个应用程序共享,并提供高可用性和一致性。
2.1简单API
ZooKeeper的一个设计目标是提供一个非常简单的编程接口,这些接口故意设计得十分简单。然而,借此接口,您可以实现更高阶的操作,如同步原语、组成员管理、所有权等。编程接口支持以下操作:
- create:在树中的某个位置创建一个节点。
- delete:删除一个节点。
- exists:测试某个位置是否存在节点。
- get data:读取节点中的数据。
- set data:向节点写入数据。
- get children:获取节点的子节点列表。
- sync:等待数据传播。
2.2 服务注册
客户端注册:当一个服务提供者实例启动时,它会向Zookeeper注册到某一路径上。
服务实例通过创建一个znode(通常是临时节点)来注册自己。这个znode包含服务实例的元数据,注册路径为:{service}/{version}/{ip:port}
示例:将我们的HelloService部署启动后,Zookeeper上会创建一个目录:
- /HelloWorldService/1.0.0/192.168.1.100:8080
下面简述服务注册的过程:
- 服务提供者启动时,会将其服务名称,ip地址注册到注册中心;
- 服务消费者在第一次调用服务时,会通过注册中心找到相应的服务的IP地址列表,并缓存到本地。当消费者调用服务时,不再请求注册中心,而是直接通过负载均衡算法从IP列表中取一个服务提供者的服务器调用服务;
- 当服务提供者的某台服务器宕机或下线时,相应的ip会从服务提供者IP列表中移除。同时,注册中心会将新的服务IP地址列表发送给消费者机器,缓存在消费者本机;
- 当某个服务的所有服务器都下线了,那么这个服务也就下线了;
- 同样,当服务提供者的某台服务器上线时,注册中心会将新的服务IP地址列表发送给服务消费者机器,缓存在消费者本机;
- 服务提供方可以根据服务消费者的数量来作为服务下线的依据
客户端代码示例:
ZooKeeper zk = new ZooKeeper("localhost:2181", 3000, null);
String path = "/services/myService";
zk.create(path, "192.168.1.100:8080".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
2.3 心跳检测机制
服务的某台机器下线时,Zookeeper是如何感知到?
zookeeper提供了“心跳检测”功能,它会定时向各个服务提供者发送一个请求(实际上建立的是一个 socket 长连接),如果长期没有响应,服务中心就认为该服务提供者已经“挂了”,并将其删除。
2.4 服务发现
有关服务发现的概念可以看我之前的文章:分布式系统架构2:服务发现
上面说了有新的服务提供者出现时,注册中心是怎么将这些变化通知给消费者客户端的呢?
有2种方式:
- 主动Pull方式:消费者定期从注册中心拉取最新的服务提供者列表,并更新本地缓存。如Eureka
- 发布-订阅模式:消费者实时监听服务更新状态,采用的是监听器+回调机制。Zookeeper用的是发布-订阅模式,客户端在服务节点设置监视器(Watcher)。当节点发生变化(如服务实例上线或下线)时,Zookeeper会通知所有设置了监视器的客户端
2.5 存在问题
Zookeeper作为注册中心不合适,缺少可用性。
zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。
而作为注册中心,可用性的要求高于一致性!
2.6 Zookeeper总结
- Zookeeper的心跳检测,可以自动探测服务提供者机器的宕机或下线;
- Zookeeper的Watch机制,可以将变更的注册列表推给服务消费者;
- Zookeeper是CP模型,不太适合作为注册中心。