聊聊 RocketMQ 名字服务

开发 项目管理
NameServer 在接收到 Broker 发送的心跳请求之后,通过默认的处理器来处理请求,保存路由信息成功后,注册成功状态返回给 Broker 服务。

NameServer 是专为 RocketMQ 设计的轻量级名字服务,它的源码非常精简,八个类 ,少于1000行代码。

图片图片

这篇文章, 笔者会从基础概念、Broker发送心跳包、NameServer 维护路由、Zookeeper vs NameServer 四个模块揭秘名字服务的设计精髓。

一、基础概念

图片图片

NameServer 是一个非常简单的 Topic 路由注册中心,其角色类似 Dubbo 中的 zookeeper ,支持 Broker 的动态注册与发现。

RocketMQ 集群工作流程:

1、NameServer 启动服务,监听 TCP 端口 , 集群多节点之间无任何信息交互,然后等待 Broker、Producer 、Consumer 连上来;

2、Broker 启动后,每隔 30 秒向所有的 NameServer 发送心跳命令 ;

3、NameServer 接收到请求之后,保存路由信息在本地内存里 ,将响应结果返给 Broker 服务;

4、Producer 启动之后,会随机的选择一个 NameServer ,并从 NameServer 中获取当前发送的 Topic 存在哪些 Broker 上,轮询从队列列表中选择一个队列,然后与队列所在的 Broker 建立长连接从而向 Broker 发消息;

5、Consumer 跟 Producer 类似,跟其中一台 NameServer 建立长连接,获取当前订阅 Topic 存在哪些 Broker 上,然后直接跟 Broker 建立连接通道,开始消费消息。

二、Broker发送心跳包

我们贴一段 Broker 发送心跳命令的源码:

图片图片

1、Broker 会每隔 30 秒向所有的 NameServer 发送心跳命令 ;

使用 CountDownLatch 实现多线程同步,可以获取发往所有的 NameServer 的心跳命令的响应结果

2、心跳命令包含两个部分:请求头和请求体

图片图片

三、NameServer 维护路由

NameServer 在接收到 Broker 发送的心跳请求之后,通过默认的处理器来处理请求,保存路由信息成功后,注册成功状态返回给 Broker 服务。

源码中,我们可以看到路由信息保存在 HashMap 中 。

图片图片

1、topicQueueTable:Topic 消息队列路由信息,包括 topic 所在的 broker 名称,读队列数量,写队列数量,同步标记等信息,rocketmq 根据 topicQueueTable 的信息进行负载均衡消息发送。

2、brokerAddrTable:Broker 节点信息,包括 brokername,所在集群名称,还有主备节点信息。

3、clusterAddrTable:Broker 集群信息,存储了集群中所有的 Brokername。

4、brokerLiveTable:Broker 状态信息,NameServer 每次收到 Broker 的心跳包就会更新该信息。

当 Broker 向 NameServer 发送心跳包(路由信息),NameServer 需要对 HashMap 进行数据更新,但我们都知道 HashMap 并不是线程安全的,高并发场景下,容易出现 CPU 100% 问题,所以更新 HashMap 时需要加锁,RocketMQ 使用了 JDK 的读写锁 ReentrantReadWriteLock 。

下面我们看下路由信息如何更新和读取:

1、写操作:更新路由信息,操作写锁

图片图片

2、读操作:查询主题信息,操作读锁

图片图片


我们可以将 NameServer 实现注册中心的方式总结为:RPC 服务 + HashMap 存储容器 + 读写锁 + 定时任务 。

1、NameServer 监听固定的端口,提供 RPC 服务

2、HashMap 作为存储容器

3、读写锁控制锁的颗粒度

4、定时任务

  • 每个 Broker 每隔 30 秒注册主题的路由信息到所有 NameServer
  • NameServer 定时任务每隔10 秒清除已宕机的 Broker , 判断宕机的标准是:当前时间减去 Broker 最后一次心跳时间大于2分钟

四、Zookeeper vs NameServer

那为什么 RocketMQ 不用 Zookeeper 做为注册中心呢 ?

我们先温习下 CAP 理论。

图片图片

CAP 理论是分布式架构中重要理论。

1、一致性( Consistency ) :所有节点在同一时间具有相同的数据 ;

2、可用性( Availability ) :保证每个请求不管成功或者失败都有响应  (某个系统的某个节点挂了,但是并不影响系统的接受或者发出请求) ;

3、分隔容忍( Partition tolerance ) :系统中任意信息的丢失或失败不会影响系统的继续运作。 (在整个系统中某个部分,挂掉了,或者宕机了,并不影响整个系统的运作或者说使用) 。

Zookeeper 是一个典型的 CP 注册中心 ,通过使 ZAB 协议来保证节点之间数据的强一致性。

笔者曾经遇到过一起神州专车服务宕机事故,zookeeper 集群不堪重负,一直在选主 。架构负责人修改了 zookeeper 的 jvm 参数,重启集群后 , 才临时解决了问题。

因为 MetaQ 集群和服务治理共用一组 zookeeper 集群 。

  • MetaQ 消费者负载均衡时,会频繁的争抢锁 ,同时也会频繁的提交 offset  ;
  • 专车的注册服务也越来越多,注册信息通过Hession 序列化存储在 zookeeper 的节点。

为了减少 zookeeper 集群的性能压力,架构团队将 MetaQ 使用的 zookeeper 集群独立出来。

这次事故让我认识到:Zookeeper 作为 CP 注册中心,大规模使用场景下,它就变得很脆弱,我们要非常小心的使用。

淘宝中间件博客出了一篇文章 :  阿里巴巴为什么不用 ZooKeeper 做服务发现 ?

文章有两个观点,笔者认为非常有借鉴意义。

1、当数据中心服务规模超过一定数量 ( 服务规模=F{服务 pub 数,服务 sub 数} ),作为注册中心的 ZooKeeper 很快就会像下图的驴子一样不堪重负。

2、可以使用 ZooKeeper,但是大数据请向左,而交易则向右,分布式协调向左,服务发现向右。

相比 ZooKeeper ,NameServer 是一个典型的 AP 注册中心,它有如下优点:

1、代码不到 1000 行,实现简单,易于维护 ;

2、性能极好,除了网络消耗,基本都是本地内存操作 ;

3、服务都是无状态,且节点之间并不交互,运维简单;

RocketMQ 的设计者之所以选择自研名字服务,遵循着架构设计的准则,笔者总结为:简单、高效、适当妥协。

责任编辑:武晓燕 来源: 勇哥java实战分享
相关推荐

2023-07-03 08:57:45

Master服务TCP

2023-05-16 08:31:09

BrokerReef版本

2024-08-19 04:00:00

2017-06-02 08:32:01

调度服务数据

2021-02-07 09:05:56

微服务结构云原生

2023-12-15 09:57:13

微服务链路服务

2022-06-02 08:21:07

RocketMQ消息中间件

2021-07-20 08:03:43

微服务应用程序

2023-02-28 08:55:33

GatewayNetty服务

2023-01-29 09:06:24

微服务划分关联

2021-07-14 06:45:49

Windows.NetTopshelf

2018-05-09 08:18:26

微服务改造架构

2022-05-09 08:34:01

FeignhttpJava

2021-06-08 09:28:12

.Net通知服务

2022-04-11 08:17:07

JVMJava进程

2018-10-15 09:39:12

服务器开发语言

2022-11-02 08:31:53

BFF架构App

2021-12-28 19:05:41

路由服务治理

2018-12-06 14:56:46

微服务隔离熔断

2024-05-31 08:45:24

点赞
收藏

51CTO技术栈公众号