MQ系列1:消息中间件执行原理

MQ系列2:消息中间件的技术选型

MQ系列3:RocketMQ 架构分析

1 关于NameServer

上一节的 MQ系列3:RocketMQ 架构分析,我们大致介绍了 RocketMQ的基本组件构成,包括 NameServer、Broker、Producer以及Consumer四部分。

NameServer,指的是服务可以根据给定的名字来进行资源或对象的地址定位,并获取有关的属性信息。在Rocket中也一样,NameServer是 RocketMQ 的服务注册中心(类似于 Kafka 集群 后面的 Zookeeper 集群一样, 对集群元数据进行管理),根据元数据(ip、port和router信息)来唯一定位服务。RocketMQ 需要先启动 NameServer ,再启动 Rocket 中的 Broker。

2 NameServer运行流程

2.1 注册

注册发生在Broker启动之后,启动后快速与NameServer建立长连接,并每30s对NameService发送一次心跳包,Broker会将自己的IP Address、Port、Router 等信息随着心跳一并注册到 NameServer中。



这里的RouterInfo 主要指Broker下包含哪些Topic信息,这种映射关系方便后面消息的生产和消费的时候进行寻址。

注册使用到的核心数据结构如下:

HashMap<String BrokerName, BrokerData> brokerAddrTable

  • HashMap 的 Key 是 Broker 的名称,存储了一个Broker服务所对应的属性信息。
  • Value 是个对象,数据结构如下:
字段 类型 说明
cluster String 所属的集群名称
broker String broker的名称
brokerAddress HashMap Broker的IP地址列表,包含一个Master IP地址列表 和 多个Slave IP地址列表
  1. " Broker-A":{
  2. "cluster":"Broker-Cluster",
  3. "brokerName":"Broker-A",
  4. "cluster":{ // 1主2从
  5. "0":"192.168.0.1:1234",
  6. "1":"192.168.0.2:1234"
  7. "2":"192.168.0.3:1234"
  8. }
  9. }

2.2 注册信息更新

当你对你的Broker中的Topic信息进行更新了(增、删、改)怎么办,你才需要重新将信息注册到NameServer中。

  • 如果你创建了新的 Topic,Broker会向 NameServer 发送注册信息,接收到信息后会对每个Master 角色的Broker ,创建一个新的QueueData对象。
  • 如果你修改了Topic,则NameServer 会先把旧的 QueueData 删除,在加一个新的 QueueData。
  • 如果你删除了Topic,则NameServer 会将对应的 QueueData 删除。

使用到的核心数据结构如下:

HashMap<String topic, List<QueueData>> topicQueueTable

  • HashMap 的 Key 是 Topic 的名称,里面存储了Topic的所有属性信息。
  • Value 是个列表,列表的数据类型是 QueueData,列表的length就是Topic中的 Master角色的 Broker 个数。
  • QueueData的结构如下
字段 类型 说明
brokerName String broker名称
readQueueNums Long 读Queue的数量
writeQueueNums Long 写Queue的数量
perm Integer 权限 PRIORITY = 3, READ = 2, WRITE = 1 , INHERIT = 0
topicSyncFlag Long 同步的位置标识
  1. {
  2. "topic-test":[ // topic名称,注意下面会用到
  3. {
  4. "brokerName":"Broker-A",
  5. "readQueueNums":37,
  6. "writeQueueNums":37,
  7. "perm":6, // 读写权限
  8. "topicSynFlag":12
  9. },
  10. {
  11. "brokerName":"Broker-B",
  12. "readQueueNums":37,
  13. "writeQueueNums":37,
  14. "perm":6, // 读写权限
  15. "topicSynFlag":12
  16. }
  17. ]
  18. }

参考RocketMQ源码如下,这边加了注释,方便理解:

  1. /**
  2. * 创建或者更新 MessageQueue 的数据
  3. * @param brokerName
  4. * @param topicConfig
  5. */
  6. private void createAndUpdateQueueData(final String brokerName, final TopicConfig topicConfig) {
  7. QueueData queueData = new QueueData();
  8. queueData.setBrokerName(brokerName); // broker 名称
  9. queueData.setWriteQueueNums(topicConfig.getWriteQueueNums()); // 读Queue的数量
  10. queueData.setReadQueueNums(topicConfig.getReadQueueNums()); // 写Queue的数量
  11. queueData.setPerm(topicConfig.getPerm()); // 权限: PRIORITY = 3, READ = 2, WRITE = 1 , INHERIT = 0
  12. queueData.setTopicSynFlag(topicConfig.getTopicSysFlag());
  13. List<QueueData> queueDataList = this.topicQueueTable.get(topicConfig.getTopicName());
  14. if (null == queueDataList) { // 新增
  15. queueDataList = new LinkedList<QueueData>();
  16. queueDataList.add(queueData);
  17. this.topicQueueTable.put(topicConfig.getTopicName(), queueDataList);
  18. log.info("new topic registerd, {} {}", topicConfig.getTopicName(), queueData);
  19. } else { // 更新
  20. boolean addNewOne = true;
  21. Iterator<QueueData> it = queueDataList.iterator();
  22. while (it.hasNext()) {
  23. QueueData qd = it.next();
  24. if (qd.getBrokerName().equals(brokerName)) {
  25. if (qd.equals(queueData)) {
  26. addNewOne = false;
  27. } else {
  28. log.info("topic changed, {} OLD: {} NEW: {}", topicConfig.getTopicName(), qd,
  29. queueData);
  30. it.remove(); // 先删除
  31. }
  32. }
  33. }
  34. if (addNewOne) {
  35. queueDataList.add(queueData); // 再添加
  36. }
  37. }
  38. }

2.3 异常清理

如果Broker挂掉,那么再被消息的生产者和消费者使用就会有问题了。这时候需要对已经宕掉的Broker进行清理,确保NamServer中注册的Broker服务信息都是Alive的。它的做法是这样的:

  • 前面我们说了,Broker每30s对NameService发送一次心跳包给NameServer
  • NameServer接收到心跳包的时候,会将当前时间戳更新到 brokerLiveTable 表的 lastUpdateTimestamp 字段中。
  • NameServer中会启动一个定时任务
  • 每10s(记住这边扫描是10s间隔,与上面心跳包区分开)扫描 一下 brokerLiveTable
  • 检查lastUpdateTimestamp字段,如果时间戳与当前时间相隔超过 120s(即两分钟),则认为 Broker 已经宕了,并会将broker清除出NameServer的注册表。

使用到的核心数据结构如下:

HashMap<String BrokerAddr, BrokerLiveInfo> brokerLiveTable

  • HashMap 的 Key 是 Broker服务器的地址信息(IP+Port),里面存储了该Broker服务器的基本信息。
  • Value 是个对象,结构如下:
字段 类型 说明
lastUpdateTimestamp Long 最后一次收到心跳包的时间戳
dataVersion DataVersion 数据版本号对象
channel Channel netty的Channel,IO数据交互媒介
haServerAddr String master地址,初次请求的时候值为空,slave向NameServer注册之后返回

2.4 消息生产和消费

上面的步骤都完成之后,NameServer这个 "中央大脑" 正式开始投入使用。这时候 ,消息的生产和消费具体是怎么做的呢?

  • Producer 或者 Consumer 启动之后会和 NameServer 建立长连接
  • 定时(默认为每30s)从 NameServer 获取Routers信息,并将路由信息保存至Producer或者Consumer的本地。
  • Producer发送一条消息 hello-brand 到 topic (topic-test) 中
  • 因为名称为 topic-test 的 topic 存在于多个 broker中,所以需要如下几个步骤,才能找到具体的地址:
    • 先 根据 topic 名称 topic-test 查询 topicQueueTable , 选择一个并获取它的broker信息(包含brokerName)
    • 再根据已经获取到的brokerName 查询 brokerAddressTable 获取具体的Broker IP地址(一般包含1个Master和n个Slave的IP地址)
    • 拿到IP地址之后,生产者与broker建立连接,并发送消息
    • 消费者同理

3 总结



上述的流程图比较清晰的描述如下运转流程:

  • NameServer 作为整个 RocketMQ 的“中央大脑” ,负责对集群元数据进行管理,所以 RocketMQ 需要先启动 NameServer 再启动 Rocket 中的 Broker。
  • Broker 启动后,与 NameServer 保持长连接,每 30s 发送一次发送心跳包,来确保Broker是否存活。并将 Broker 信息 ( IP+、端口等信息)以及Broker中存储的Topic信息上报。注册成功后,NameServer 集群中就有 Topic 跟 Broker 的映射关系。
  • NameServer有个定时任务,每10s扫描下brokerLiveTable表 , 如果检测到某个Broker 宕机(因为使用心跳机制, 如果检测超120s(两分钟)无上报心跳),则从路由注册表中将其移除。
  • 生产者在发送某个主题的消息之前先从 NamerServer 获取 Broker 服务器地址列表(通过topic名称查询topicQueueTable获得broker名称,通过broker名称查询brokerAddressTable获取具体的Broker IP地址),然后根据负载均衡算法从列表中选择1台Broker ,建立连接通道,进行消息发送。
  • 消费者在订阅某个topic的消息之前从 NamerServer 获取 Broker 服务器地址列表(同上),包括关联的全部Topic队列信息。进而获取当前订阅 Topic 存在哪些 Broker 上,然后直接跟 Broker 建立连接通道,开始消费数据。
  • 生产者和消费者默认每30s 从 NamerServer 获取 Broker 服务器地址列表,以及关联的所有Topic队列信息,更新到Client本地。

参考:

https://zhuanlan.zhihu.com/p/388807516

MQ系列4:NameServer 原理解析的更多相关文章

  1. MQ系列5:RocketMQ消息的发送模式

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 在之前的篇章中,我们学习了RocketMQ的原理, ...

  2. MQ系列6:消息的消费

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 在之前 ...

  3. java基础解析系列(七)---ThreadLocal原理分析

    java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...

  4. java基础解析系列(六)---注解原理及使用

    java基础解析系列(六)---注解原理及使用 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---Integer缓存及 ...

  5. Spring Boot干货系列:(三)启动原理解析

    Spring Boot干货系列:(三)启动原理解析 2017-03-13 嘟嘟MD 嘟爷java超神学堂 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说 ...

  6. ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析

    ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析 上一篇:ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解 ...

  7. ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解析

    ThreadLocal系列之InheritableThreadLocal的使用及原理解析(源码基于java8) 上一篇:ThreadLocal系列(一)-ThreadLocal的使用及原理解析 下一篇 ...

  8. ThreadLocal系列(一)-ThreadLocal的使用及原理解析

    ThreadLocal系列之ThreadLocal(源码基于java8) 项目中我们如果想要某个对象在程序运行中的任意位置获取到,就需要借助ThreadLocal来实现,这个对象称作线程的本地变量,下 ...

  9. android黑科技系列——应用市场省流量更新(增量升级)原理解析

    一.前言 最近在看热修复相关的框架,之前我们已经看过了阿里的Dexposed和AndFix这两个框架了,不了解的同学可以点击这里进行查看:Dexposed框架原理解析 和 AndFix热修复框架原理解 ...

随机推荐

  1. 开发工具-SQL Server官方下载地址

    更新记录 2022年6月10日 完善标题. https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 相关链接: SSMS下载地址 ...

  2. redisson之分布式锁实现原理(三)

    官网:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 一.什么是分布式锁 1.1.什么是分布式锁 分布式锁,即分布式系统中的锁 ...

  3. vue基本原理

    当一个Vue实例创建时,Vue会遍历data中的属性,用Object.defineProperty(vue3.0使用proxy)将它们转为getter/setter,并且在内部追踪相关依赖,在属性被访 ...

  4. Spring jdbctemplate和事务管理器

    内部bean 对象结构: @Autowiredprivate IAccountService accountService; @Service("accountService")@ ...

  5. UiPath循环活动For Each的介绍和使用

    一.For Each的介绍 For Each:循环迭代一个列表.数组.或其他类型的集合, 可以遍历并分别处理每条信息 二.For Each在UiPath中的使用 1.  打开设计器,在设计库中新建一个 ...

  6. SpringBoot 开发案例之整合FastDFS分布式文件系统

    1.pom依赖 <!--fastdfs--> <dependency> <groupId>com.github.tobato</groupId> < ...

  7. 52 条 SQL 语句性能优化策略,建议收藏

    本文会提到 52 条 SQL 语句性能优化策略. 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引. 2.应尽量避免在where子句中对字段进行nul ...

  8. MVCC - Read View的可见性判断理解

    读了 @SnailMann大佬[MySQL笔记]正确的理解MySQL的MVCC及实现原理 收益颇丰,非常感谢! 但对其中如何判断事务是否可见性还是不太理解,于是作了本文,在原博客基础上,举例画图论证. ...

  9. mybatis collection解析以及和association的区别

    1.collection标签 说到mybatis的collection标签,我们肯定不陌生,可以通过它解决一对多的映射问题,举个例子一个用户对应多个系统权限,通过对用户表和权限表的关联查询我们可以得到 ...

  10. python+tkinter 的布局

    from tkinter import * win = Tk() win.title("布局") # #窗口标题 win.geometry("600x500+200+20 ...