RocketMQ的NameServer

NameServer的设计:

(1)NameServer互相独立,彼此没有通信关系,单台NameServer挂掉,不影响其他NameServer。

(2) NameServer不去连接别的机器,不主动推消息。

(3)单个Broker (Master、Slave)与所有NameServer进行定时注册,以便告知NameServer自己还活着。

Broker每隔30秒向所有NameServer发送心跳,心跳包含了自身的topic配置信息。

NameServer每隔10秒,扫描所有还存活的broker连接,如果某个连接的最后更新时间

与当前时间差值超过2分钟,则断开此连接,NameServer也会断开此broker下所有与slave的连接。同时更新topic与队列的对应关系,但不通知生产者和消费者。

Broker slave同步或者异步从Broker master上拷贝数据。

(4)Consumer随机与一个NameServer建立长连接,如果该NameServer断开,则从NameServer列表中查找下一个进行连接。

Consumer主要从NameServer中根据Topic查询Broker的地址,查到就会缓存到客户端,并向提供Topic服务的Master、Slave建立长连接,且定时向Master、Slave发送心跳。

如果Broker宕机,则NameServer 会将其剔除,而Consumer端的定时任务MQClientInstance.this.updateTopicRouteInfoFromNameserver每30秒执行一次,将Topic对应的Broker地址拉取下来,此地址只有Slave地址了,此时Consumer从Slave上消费。

消费者与Master和Slave都建有连接,在不同场景有不同的消费规则。

(5)Producer随机与一个NameServer建立长连接,每隔30秒(此处时间可配置)从NameServer获取Topic的最新队列情况,如果某个Broker Master宕机,Producer最多30秒才能感知,在这个期间,发往该broker master的消息失败。Producer向提供Topic服务的Master建立长连接,且定时向Master发送心跳。

生产者与所有的master连接,但不能向slave写入。

客户端是先从NameServer寻址的,得到可用Broker的IP和端口信息,然后据此信息连接broker。

综上所述,NameServer在RocketMQ中的作用:

(1)NameServer用来保存活跃的broker列表,包括Master和Slave。

(2)NameServer用来保存所有topic和该topic 所有队列的列表。

(3)NameServer用来保存所有broker的Filter 列表。

(4)命名服务器为客户端,包括生产者,消费者和命令行客户端提供最新的路由信息。

RocketMQ为什么不使用ZooKeeper而自己开发NameServer?

在服务发现领域,ZooKeeper 根本就不能算是最佳的选择。

(1)注册中心是CP还是AP系统?

在分布式系统中,即使是对等部署的服务,因为请求到达的时间,硬件的状态,操作系统的调度,虚拟机的GC等,任何一个时间点,这些对等部署的节点状态也不可能完全一致,而流量不一致的情况下,只要注册中心在A承诺的时间内(例如1s内)将数据收敛到一致状态(即满足最终一致),流量将很快趋于统计学意义上的一致,所以注册中心以最终一致的模型设计在生产实践中完全可以接受。

(2)分区容忍及可用性需求分析实践中,注册中心不能因为自身的任何原因破坏服务之间本身的可连通性,这是注册中心设计应该遵循的铁律!

在CAP的权衡中,注册中心的可用性比数据强一致性更宝贵,所以整体设计更应该偏向AP,而非CP,数据不一致在可接受范围,而P下舍弃A却完全违反了注册中心不能因为自身的任何原因破坏服务本身的可连通性的原则。

(3)服务规模、容量、服务联通性

当数据中心服务规模超过一定数量,作为注册中心的ZooKeeper性能堪忧。

在服务发现和健康监测场景下,随着服务规模的增大,无论是应用频繁发布时的服务注册带来的写请求,还是刷毫秒级的服务健康状态带来的写请求,还是恨不能整个数据中心的机器或者容器皆与注册中心有长连接带来的连接压力上,ZooKeeper很快就会力不从心,而ZooKeeper的写并不是可扩展的,不可以通过加节点解决水平扩展性问题。

(4)注册中心需要持久存储和事务日志么?需要,也不需要。

在服务发现场景中,其最核心的数据——实时的健康的服务的地址列表,真的需要数据持久化么?不需要

在服务发现中,服务调用发起方更关注的是其要调用的服务的实时的地址列表和实时健康状态,每次发起调用时,并不关心要调用的服务的历史服务地址列表、过去的健康状态。

但是一个完整的生产可用的注册中心,除了服务的实时地址列表以及实时的健康状态之外,还会存储一些服务的元数据信息,例如服务的版本,分组,所在的数据中心,权重,鉴权策略信息,服务标签等元数据,这些数据需要持久化存储,并且注册中心应该提供对这些元数据的检索的能力。

(5)服务健康检查

使用ZooKeeper作为服务注册中心时,服务的健康检测绑定在了ZooKeeper对于Session的健康监测上,或者说绑定在TCP长链接活性探测上了。

ZK与服务提供者机器之间的TCP长链接活性探测正常的时候,该服务就是健康的么?答案当然是否定的!注册中心应该提供更丰富的健康监测方案,服务的健康与否的逻辑应该开放给服务提供方自己定义,而不是一刀切搞成了TCP活性检测!

健康检测的一大基本设计原则就是尽可能真实的反馈服务本身的真实健康状态,否则一个不敢被服务调用者相信的健康状态判定结果还不如没有健康检测。

(5)注册中心的容灾考虑

如果注册中心(Registry)本身完全宕机了,服务调用链路应该受到影响么?不应该受到影响。

服务调用(请求响应流)链路应该是弱依赖注册中心,必须仅在服务发布,机器上下线,服务扩缩容等必要时才依赖注册中心。

这需要注册中心仔细的设计自己提供的客户端,客户端中应该有针对注册中心服务完全不可用时做容灾的手段,例如设计客户端缓存数据机制就是行之有效的手段。另外,注册中心的健康检查机制也要仔细设计以便在这种情况不会出现诸如推空等情况的出现。

ZooKeeper的原生客户端并没有这种能力,所以利用ZooKeeper实现注册中心的时候我们一定要问自己,如果把ZooKeeper所有节点全干掉,你生产上的所有服务调用链路能不受任何影响么?

阿里巴巴是不是完全没有使用ZooKeeper?并不是。

熟悉阿里巴巴技术体系的都知道,其实阿里巴巴维护了目前国内最大规模的ZooKeeper集群,整体规模有近千台的ZooKeeper服务节点。

在粗粒度分布式锁,分布式选主,主备高可用切换等不需要高TPS支持的场景下有不可替代的作用,而这些需求往往多集中在大数据、离线任务等相关的业务领域,因为大数据领域,讲究分割数据集,并且大部分时间分任务多进程/线程并行处理这些数据集,但是总是有一些点上需要将这些任务和进程统一协调,这时候就是ZooKeeper发挥巨大作用的用武之地。

但是在交易场景交易链路上,在主业务数据存取,大规模服务发现、大规模健康监测等方面有天然的短板,应该竭力避免在这些场景下引入ZooKeeper,在阿里巴巴的生产实践中,应用对ZooKeeper申请使用的时候要进行严格的场景、容量、SLA需求的评估.