Ref: https://xie.infoq.cn/article/2b0ab46b6582a3523a1f3915f
NameServer 是一个非常简单的 Topic 路由注册中心,其角色类似 Dubbo 中的 zookeeper,支持 Broker 的动态注册与发现。主要包括两个功能:
- Broker 管理:NameServer 接受 Broker 集群的注册信息并且保存下来作为路由信息的基本数据。然后提供心跳检测机制,检查 Broker 是否还存活;
- 路由信息管理:每个 NameServer 将保存关于 Broker 集群的整个路由信息和用于客户端查询的队列信息。然后 Producer 和 Conumser 通过 NameServer 就可以知道整个 Broker 集群的路由信息,从而进行消息的投递和消费。
NameServer 通常也是集群的方式部署,各实例间相互不进行信息通讯。Broker 是向每一台 NameServer 注册自己的路由信息,所以每一个 NameServer 实例上面都保存一份完整的路由信息。当某个 NameServer 因某种原因下线了,Broker 仍然可以向其它 NameServer 同步其路由信息,Producer,Consumer 仍然可以动态感知 Broker 的路由的信息。
对于上述说明的个人理解,我可以把整个 RocketMQ 集群看作是微服务,由多组 Broker 提供服务,生产者和消费者实例调用该服务,而 NameServer 的本质上就是一个轻量级的服务发现与注册中心,生产者和消费者需要通过向 NameServer 请求路由表才能找到消息所在的 Broker 地址,同样的,Broker 也需要将自身注册到 NameServer,才能被生产者和消费者找到。
目前服务发现的组件有很多,如 etcd、consul 和 zookeeper 等,为什么 RocketMQ 要自己开发服务注册中心 NameServer,而不是直接使用这些开源组件呢?尤其是 RocketMQ 设计之初时参考的 Kafka 就一直是使用 Zookeeper 作为服务注册中心的,它提供了 Master 选举、分布式锁、数据的发布和订阅等诸多功能。其实在 RocketMQ 的早期版本,即 MetaQ 1.x 和 MetaQ 2.x 阶段,也是依赖 Zookeeper 的。但 MetaQ 3.x(即 RocketMQ)却去掉了 ZooKeeper 依赖,转而采用自己的 NameServer。
这么做的原因是,RocketMQ 的架构设计决定了只需要一个轻量级的元数据服务器就足够了,只需要保持最终一致,而不需要 Zookeeper 这样的强一致性解决方案,不需要再依赖另一个中间件,从而减少整体维护成本。根据 CAP 理论,RocketMQ 在名称服务这个模块的设计上选择了 AP,而不是 CP:
- 一致性 (Consistency):Name Server 集群中的多个实例,彼此之间是不通信的,这意味着某一时刻,不同实例上维护的元数据可能是不同的,客户端获取到的数据也可能是不一致的。
- 可用性 (Availability):只要不是所有 NameServer 节点都挂掉,且某个节点可以在指定时间之间内响应客户端即可。
- 分区容错 (Partiton Tolerance):对于分布式架构,网络条件不可控,出现网络分区是不可避免的,只要保证部分 NameServer 节点网络可达,就可以获取到数据。具体看公司如何实施,例如:为了实现跨机房的容灾,可以将 NameServer 部署的不同的机房,某个机房出现网络故障,其他机房依然可用,当然 Broker 集群 / Producer 集群 / Consumer 集群也要跨机房部署。
事实上,除了 RocketMQ 开发了自己的 NameServer,最近 Kafka 社区也发布了文章表示将在 2.8 版本消除 Kafka 对 ZooKeeper 的依赖,该提案建议用自管理的元数据仲裁机制替换原来的 ZooKeeper 组件。