@@ -3469,19 +3469,7 @@ RCVBUF_ALLOCATOR:属于 SocketChannal 参数 |
3469 | 3469 |
|
3470 | 3470 |  |
3471 | 3471 |
|
3472 | | -主要缺点包含以下几点: |
3473 | 3472 |
|
3474 | | -* 系统可用性降低:系统引入的外部依赖越多,系统稳定性越差,一旦 MQ 宕机,就会对业务造成影响 |
3475 | | - |
3476 | | - 引申问题:如何保证 MQ 的高可用? |
3477 | | - |
3478 | | -* 系统复杂度提高:MQ 的加入大大增加了系统的复杂度,以前系统间是同步的远程调用,现在是通过 MQ 进行异步调用 |
3479 | | - |
3480 | | - 引申问题:如何保证消息没有被重复消费?怎么处理消息丢失情况?那么保证消息传递的顺序性? |
3481 | | - |
3482 | | -* 一致性问题:A 系统处理完业务,通过 MQ 给 B、C、D 三个系统发消息数据,如果 B 系统、C 系统处理成功,D 系统处理失败 |
3483 | | - |
3484 | | - 引申问题:如何保证消息数据处理的一致性? |
3485 | 3473 |
|
3486 | 3474 |
|
3487 | 3475 |
|
@@ -4528,7 +4516,7 @@ Broker 包含了以下几个重要子模块: |
4528 | 4516 | RocketMQ 的工作流程: |
4529 | 4517 |
|
4530 | 4518 | - 启动 NameServer 监听端口,等待 Broker、Producer、Consumer 连上来,相当于一个路由控制中心 |
4531 | | -- Broker 启动,跟所有的 NameServer 保持长连接,每隔 30s 时间向 NameServer 上报 Topic 路由信息(心跳包)。心跳包中包含当前 Broker 信息(IP、端口等)以及存储所有 Topic 信息。注册成功后,NameServer 集群中就有 Topic 跟 Broker 的映射关系 |
| 4519 | +- Broker 启动,跟**所有的 NameServer 保持长连接**,每隔 30s 时间向 NameServer 上报 Topic 路由信息(心跳包)。心跳包中包含当前 Broker 信息(IP、端口等)以及存储所有 Topic 信息。注册成功后,NameServer 集群中就有 Topic 跟 Broker 的映射关系 |
4532 | 4520 | - 收发消息前,先创建 Topic,创建 Topic 时需要指定该 Topic 要存储在哪些 Broker 上,也可以在发送消息时自动创建 Topic |
4533 | 4521 | - Producer 启动时先跟 NameServer 集群中的**其中一台**建立长连接,并从 NameServer 中获取当前发送的 Topic 存在哪些 Broker 上,同时 Producer 会默认每隔 30s 向 NameServer **定时拉取**一次路由信息 |
4534 | 4522 | - Producer 发送消息时,根据消息的 Topic 从本地缓存的 TopicPublishInfoTable 获取路由信息,如果没有则会从 NameServer 上重新拉取并更新,轮询队列列表并选择一个队列 MessageQueue,然后与队列所在的 Broker 建立长连接,向 Broker 发消息 |
@@ -4698,9 +4686,9 @@ RocketMQ 网络部署特点: |
4698 | 4686 |
|
4699 | 4687 | - NameServer 是一个几乎**无状态节点**,节点之间相互独立,无任何信息同步 |
4700 | 4688 |
|
4701 | | -- Broker 部署相对复杂,Broker 分为 Master 与 Slave,Master 可以部署多个,一个 Master 可以对应多个 Slave,但是一个 Slave 只能对应一个 Master,Master 与 Slave 的对应关系通过指定相同 BrokerName、不同 BrokerId 来定义,**BrokerId 为 0 是 Master,非 0 表示 Slave。每个 Broker 与 NameServer 集群中的所有节点建立长连接**,定时注册 Topic 信息到所有 NameServer |
| 4689 | +- Broker 部署相对复杂,Broker 分为 Master 与 Slave,Master 可以部署多个,一个 Master 可以对应多个 Slave,但是一个 Slave 只能对应一个 Master,Master 与 Slave 的对应关系通过指定相同 BrokerName、不同 BrokerId 来定义,BrokerId 为 0 是 Master,非 0 表示 Slave。**每个 Broker 与 NameServer 集群中的所有节点建立长连接**,定时注册 Topic 信息到所有 NameServer |
4702 | 4690 |
|
4703 | | - 注意:部署架构上也支持一 Master 多 Slave,但只有 BrokerId=1 的从服务器才会参与消息的读负载(读写分离) |
| 4691 | + 说明:部署架构上也支持一 Master 多 Slave,但只有 BrokerId=1 的从服务器才会参与消息的读负载(读写分离) |
4704 | 4692 |
|
4705 | 4693 | - Producer 与 NameServer 集群中的其中**一个节点(随机选择)建立长连接**,定期从 NameServer 获取 Topic 路由信息,并向提供 Topic 服务的 Master 建立长连接,且定时向 Master **发送心跳**。Producer 完全无状态,可集群部署 |
4706 | 4694 |
|
@@ -4710,8 +4698,6 @@ RocketMQ 网络部署特点: |
4710 | 4698 |
|
4711 | 4699 |  |
4712 | 4700 |
|
4713 | | -集群工作流程:参考通信机制 → 工作流程 |
4714 | | - |
4715 | 4701 |
|
4716 | 4702 |
|
4717 | 4703 | 官方文档:https://github.com/apache/rocketmq/blob/master/docs/cn/architecture.md |
@@ -4722,12 +4708,12 @@ RocketMQ 网络部署特点: |
4722 | 4708 |
|
4723 | 4709 |
|
4724 | 4710 |
|
4725 | | -#### 高可用 |
4726 | | - |
4727 | | -在 Consumer 的配置文件中,并不需要设置是从 Master 读还是从 Slave 读,当 Master 不可用或者繁忙的时候,Consumer 会被自动切换到从 Slave 读。有了自动切换的机制,当一个 Master 机器出现故障后,Consumer 仍然可以从 Slave 读取消息,不影响 Consumer 程序,达到了消费端的高可用性 |
| 4711 | +#### 高可用性 |
4728 | 4712 |
|
4729 | 4713 | 在创建 Topic 的时候,把 Topic 的多个 Message Queue 创建在多个 Broker 组上(相同 Broker 名称,不同 brokerId 的机器组成一个 Broker 组),当一个 Broker 组的 Master 不可用后,其他组的 Master 仍然可用,Producer 仍然可以发送消息 |
4730 | 4714 |
|
| 4715 | +在 Consumer 的配置文件中,并不需要设置是从 Master 读还是从 Slave 读,当 Master 不可用或者繁忙的时候,Consumer 会被自动切换到从 Slave 读。有了自动切换的机制,当一个 Master 机器出现故障后,Consumer 仍然可以从 Slave 读取消息,不影响 Consumer 程序,达到了消费端的高可用性 |
| 4716 | + |
4731 | 4717 | RocketMQ 目前还不支持把 Slave 自动转成 Master,需要手动停止 Slave 角色的 Broker,更改配置文件,用新的配置文件启动 Broker |
4732 | 4718 |
|
4733 | 4719 |  |
@@ -6132,6 +6118,8 @@ MappedFileQueue 用来管理 MappedFile 文件 |
6132 | 6118 | private volatile long beginTimeInLock = 0; // 写数据时加锁的开始时间 |
6133 | 6119 | protected final PutMessageLock putMessageLock; // 写锁,两个实现类:自旋锁和重入锁 |
6134 | 6120 | ``` |
| 6121 | + |
| 6122 | + 因为发送消息是需要持久化的,在 Broker 端持久化时会获取该锁,**保证发送的消息的线程安全** |
6135 | 6123 |
|
6136 | 6124 | 构造方法: |
6137 | 6125 |
|
@@ -6805,7 +6793,7 @@ GroupTransferService 用来控制数据同步 |
6805 | 6793 |
|
6806 | 6794 | ###### 成员属性 |
6807 | 6795 |
|
6808 | | -HAClient 是 slave 端运行的代码,用于和 master 服务器建立长连接,上报本地同步进度,消费服务器发来的 msg 数据 |
| 6796 | +HAClient 是 slave 端运行的代码,用于**和 master 服务器建立长连接**,上报本地同步进度,消费服务器发来的 msg 数据 |
6809 | 6797 |
|
6810 | 6798 | 成员变量: |
6811 | 6799 |
|
@@ -7225,8 +7213,6 @@ WriteSocketService 类是一个任务对象,master向 slave 传输的数据帧 |
7225 | 7213 |
|
7226 | 7214 | * `if (writeSize > 0)`:写数据成功,但是不代表 SMBR 中的数据全部写完成 |
7227 | 7215 |
|
7228 | | - |
7229 | | - |
7230 | 7216 | * `boolean result`:判断是否发送完成,返回该值 |
7231 | 7217 |
|
7232 | 7218 |
|
@@ -7947,7 +7933,7 @@ DefaultMQProducerImpl 类是默认的生产者实现类 |
7947 | 7933 | private SendResult sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout) |
7948 | 7934 | ``` |
7949 | 7935 |
|
7950 | | - * `brokerAddr = this.mQClientFactory(...)`:获取指定 BrokerName 对应的 mater 节点的地址,master 节点的 ID 为 0 |
| 7936 | + * `brokerAddr = this.mQClientFactory(...)`:**获取指定 BrokerName 对应的 mater 节点的地址**,master 节点的 ID 为 0,集群模式下,**发送消息要发到主节点** |
7951 | 7937 |
|
7952 | 7938 | * `brokerAddr = MixAll.brokerVIPChannel()`:Broker 启动时会绑定两个服务器端口,一个是普通端口,一个是 VIP 端口,服务器端根据不同端口创建不同的的 NioSocketChannel |
7953 | 7939 |
|
@@ -8374,7 +8360,7 @@ MQClientInstance 是 RocketMQ 客户端实例,在一个 JVM 进程中只有一 |
8374 | 8360 | MQClientInstance.this.adjustThreadPool(); |
8375 | 8361 | ``` |
8376 | 8362 |
|
8377 | | -* updateTopicRouteInfoFromNameServer():**更新路由数据** |
| 8363 | +* updateTopicRouteInfoFromNameServer():**更新路由数据**,通过加锁保证当前实例只有一个线程去更新 |
8378 | 8364 |
|
8379 | 8365 | * `if (isDefault && defaultMQProducer != null)`:需要默认数据 |
8380 | 8366 |
|
@@ -8388,13 +8374,15 @@ MQClientInstance 是 RocketMQ 客户端实例,在一个 JVM 进程中只有一 |
8388 | 8374 |
|
8389 | 8375 | * `if (changed)`:不一致进入更新逻辑 |
8390 | 8376 |
|
8391 | | - `Update Pub info`:更新生产者信息 |
| 8377 | + `this.brokerAddrTable.put(...)`:更新客户端 broker 物理**节点映射表** |
8392 | 8378 |
|
| 8379 | + `Update Pub info`:更新生产者信息 |
| 8380 | + |
8393 | 8381 | * `publishInfo = topicRouteData2TopicPublishInfo(topic, topicRouteData)`:将主题路由数据转化为发布数据,会**创建消息队列 MQ**,放入发布数据对象的集合中 |
8394 | 8382 | * `impl.updateTopicPublishInfo(topic, publishInfo)`:生产者将主题的发布数据保存到它本地,方便发送消息使用 |
8395 | | - |
8396 | | - `Update sub info`:更新消费者信息,创建 MQ 队列,更新订阅信息,用于负载均衡 |
8397 | 8383 |
|
| 8384 | + `Update sub info`:更新消费者信息,创建 MQ 队列,更新订阅信息,用于负载均衡 |
| 8385 | + |
8398 | 8386 | `this.topicRouteTable.put(topic, cloneTopicRouteData)`:**将数据放入本地路由表** |
8399 | 8387 |
|
8400 | 8388 |
|
@@ -9537,7 +9525,7 @@ PullAPIWrapper 类封装了拉取消息的 API |
9537 | 9525 |
|
9538 | 9526 | * pullKernelImpl():拉消息 |
9539 | 9527 |
|
9540 | | - * `FindBrokerResult findBrokerResult`:查询指定 BrokerName 的地址信息,主节点或者推荐节点 |
| 9528 | + * `FindBrokerResult findBrokerResult`:**本地查询指定 BrokerName 的地址信息**,推荐节点或者主节点 |
9541 | 9529 |
|
9542 | 9530 | * `if (null == findBrokerResult)`:查询不到,就到 Namesrv 获取指定 topic 的路由数据 |
9543 | 9531 |
|
@@ -9603,7 +9591,7 @@ private RemotingCommand processRequest(final Channel channel, RemotingCommand re |
9603 | 9591 |
|
9604 | 9592 | * `switch (this.brokerController.getMessageStoreConfig().getBrokerRole())`:如果当前主机节点角色为 slave 并且**从节点读**并未开启的话,直接给客户端 一个状态 `PULL_RETRY_IMMEDIATELY`,并设置为下次从主节点读 |
9605 | 9593 |
|
9606 | | -* `if (this.brokerController.getBrokerConfig().isSlaveReadEnable())`:消费太慢,下次从另一台机器拉取 |
| 9594 | +* `if (this.brokerController.getBrokerConfig().isSlaveReadEnable())`:消费太慢,**下次从另一台机器拉取** |
9607 | 9595 |
|
9608 | 9596 | * `switch (getMessageResult.getStatus())`:根据 getMessageResult 的状态设置 response 的 code |
9609 | 9597 |
|
|