RocketMQ实战与原理解析读书笔记

第1章 快速入门
1.3.1 RocketMQ 的下载、安装和配置
RocketMQ 的 Binary 版是一些编译好的 jar 和辅助的 shell 脚本,也可以下载源码自己编译。Binary下载完成之后里面的目录结构和普通的项目结构差不多, bin conf等
1.3.2 启动消息队列服务
启动单机的消息队列服务比较简单,不需要写配置文件,只需要依次启动
本机的 NameServer 和 Broker 即可。
启动 NameServer:

nohup sh bin/mqnamesrv &
tail - f ~/Logs/ rocketmqLogs/ namesrv . Log
The Name Server boot success . …
启动 Broker:
nohup sh bin/mqbroker n localhost : 9876&
tail -f ~/ Logs/rocketmqLogs/broker . Log
The broker[%s, 192.168.0.233 : 10911] boot success … .
1.3.4 关闭消息队列
关闭 NameServer 和 Broker:
sh bin/mqshutdown broker
The mqbroker (36695) i s running …
Send shutdown request to mqbroker (36695) OK

sh bin/mqshutdown namesrv
The mqnamesr v (36664 ) is running …
Send shutdown request t o mqnamesrv (36664) OK
第2章 生产环境下的配置和使用
2.1RocketMQ各部分角色介绍
四个角色就是 Producer、 Consumer、 Broker 和 NameServer

启动 RocketMQ 的顺序是先启动 NameServer,再启动 Broker,使用 Producer 来发送,使用 Consumer 来接收

为了消除单点故障,增加可靠性或增大吞吐量,可以在多台机器上部署多个 NameServer 和 Broker,为每个Broker 部署一个或多个 Slave。

Topic 和 Message Queue
不同类型的消息以不同的 Topic 名称来区分。
所以发送和接收消息前,先创建 Topic, 针对某个Topic发送和接收消息。

如果需要能支持增加并行处理的机器来提高处理速度,这时候一个 Topic 可以根据需求设置一个或多个 Message Queue, Message Queue 类似分区或 Partition。

Topic 有了多个 Message Queue 后,消息可以并行地向各个Message Queue 发送,消费者也可以并行地从多个 Message Queue 读取消息并消费。

Producer
消息生产者,负责产生消息,一般由业务系统负责产生消息。

Consumer
消息消费者,负责消费消息,一般是后台系统负责异步消费。

Push Consumer
Consumer 的一种,应用通常向 Consumer 对象注册一个 Listener 接口,一旦收到消息,Consumer 对象立刻回调 Listener 接口方法。

Pull Consumer
Consumer 的一种,应用通常主动调用 Consumer 的拉消息方法从 Broker 拉消息,主动权由应用控制。

Producer Group
一类 Producer 的集合名称,这类 Producer 通常发送一类消息,且发送逻辑一致。

Consumer Group
一类 Consumer 的集合名称,这类 Consumer 通常消费一类消息,且消费逻辑一致。

Broker
消息中转角色,负责存储消息,转发消息,一般也称为 Server。在 JMS 规范中称为 Provider。

广播消费
一条消息被多个 Consumer 消费,即使这些 Consumer 属于同一个 Consumer Group,消息也会被 Consumer Group 中的每个 Consumer 都消费一次,广播消费中的 Consumer Group 概念可以认为在消息划分方面无意 义。 在 CORBA Notification 规范中,消费方式都属于广播消费。 在 JMS 规范中,相当于 JMS publish/subscribe model

集群消费
一个 Consumer Group 中的 Consumer 实例平均分摊消费消息。例如某个 Topic 有 9 条消息,其中一个 Consumer Group 有 3 个实例(可能是 3 个进程,或者 3 台机器),那么每个实例只消费其中的 3 条消息。 在 CORBA Notification 规范中,无此消费方式。
在 JMS 规范中,JMS point-to-point model 与之类似,但是 RocketMQ 的集群消费功能大等于 PTP 模型。 因为 RocketMQ 单个 Consumer Group 内的消费者类似于 PTP,但是一个 Topic/Queue 可以被多个 Consumer Group 消费。

顺序消息
消费消息的顺序要同发送消息的顺序一致,在 RocketMQ 中,主要指的是局部顺序,即一类消息为满足顺
序性,必须 Producer 单线程顺序发送,且发送到同一个队列,这样 Consumer 就可以按照 Producer 发送的顺序去消费消息。

普通顺序消息
顺序消息的一种,正常情况下可以保证完全的顺序消息,但是一旦发生通信异常,Broker 重启,由于队列
总数发生变化,哈希取模后定位的队列会变化,产生短暂的消息顺序不一致。
如果业务能容忍在集群异常情况(如某个 Broker 宕机或者重启)下,消息短暂的乱序,使用普通顺序方
式比较合适。

严格顺序消息
顺序消息的一种,无论正常异常情况都能保证顺序,但是牺牲了分布式 Failover 特性,即 Broker 集群中只
要有一台机器不可用,则整个集群都不可用,服务可用性大大降低。
如果服务器部署为同步双写模式,此缺陷可通过备机自动切换为主避免,不过仍然会存在几分钟的服务不可用。(依赖同步双写,主备自动切换,自动切换功能目前还未实现)
目前已知的应用只有数据库 binlog 同步强依赖严格顺序消息,其他应用绝大部分都可以容忍短暂乱序,推荐使用普通的顺序消息。

Message Queue
在 RocketMQ 中,所有消息队列都是持久化,长度无限的数据结构,所谓长度无限是指队列中的每个存储单元都是定长,访问其中的存储单元使用 Offset 来访问,offset 为 java long 类型,64 位,理论上在 100年内不会溢出,所以认为是长度无限,另外队列中只保存最近几天的数据,之前的数据会按照过期时间来删除。 也可以认为 Message Queue 是一个长度无限的数组,offset就是下标

2.2 多机集群配置和部署
2.2.1 启动多个 NameServer 和 Broker
两台物理机下多集群配置
首先在这两台机器上分别启动 NameServer, 这样我们就得到了一个无单点的 NameServer 服务
然后启动 Broker,每台机器上都要分别启动一个 Master 角色的 Broker 和一个 Slave 角色的 Broker,并互为主备, 可以基于 RocketMQ 自带的示例配置文件写自己的配置文件(示例配置文件在 conf/2m-2s-sync 目录下)。

1 ) 192.168.100.131机器上 Master Broker 的配置文件:
namesrvAddr=192.168.100.131:9876; 192.168.100.132:9876
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=0
deletewhen=04
fileReservedTime=48
brokerRole=SYNC_MASTER
flushDiskType=ASYNC_FLUSH
listenPort=10911
storePathRootDir=/ home / rocketmq / store-a

2 ) 192.168.100.132机器上 Master Broker的配置文件:
namesrvAddr=192.168.100.131:9876; 192.168.100.132 :9876
brokerClusterName=DefaultCluster
brokerName=broker-b
brokerId=0
deletewhen=04
fileReservedTime=4 8brokerRole=SYNC_MASTER
flushDiskType=ASYNC_FLUSH
listenPort=10911
storePathRootDir= / home /rocketmq / store-b

3 ) 192.168.100.131机器上 Slave Broker的配置文件:
namesrvAddr=192.168.100.131:9876; 192.168.100.132:9876
brokerClusterName=DefaultCluster
brokerName=broker-b
brokerId=1
deletewhen=04
fileReservedTime=48brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
listenPort=11011
storePathRootDir=/ home / rocketmq/ store-b

4 ) 192.168.100.132机器上 Slave Broker 的配置文件:
namesrvAddr=192.168.100.131 :9876; 192.168.100.132:9876
brokerClusterName=DefaultCluster
brokerName=broker-a
brokerId=1
deletewhen=04
fileReservedTime=48brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
listenPort=11011
storePathRootDir=/ home / rocketmq / store-a

然后分别使用如下命令启动四个Broker :
nohup sh ./ bin/mgbroker -c config_file &

这样一个高可用的RocketMQ集群就搭建好了,还可以在一台机器上启动rocketmq-console,比如在192.168.100.131上启动RocketMQ-console,然后在浏览器中输人地址192.168.100.131:8080,这样就可以可视化地查看集群状态了。
2.2.2 配置参数介绍
本节将逐个介绍 Broker 配置文件中用到的参数含义:
1)namesrvAddr=192.168.100.131:9876; 192.168.100.132:9876
NamerServer 的地址,可以是多个。

  1. brokerCl usterName=Defaul tCl uster
    Cluster 的地址,如果集群机器数比较多,可以分成多个 Cluster,每个Cluster 供一个业务群使用。

  2. brokerName=broker- a
    Broker 的名称, Master 和 Slave 通过使用相同的 Broker 名称来表明相互关系,以说明某个 Slave 是哪个 Master 的 Slave。

  3. brokerid=0
    一个 Master Barker 可以有多个 Slave, 0 表示 Master,大于 0 表示不同Slave 的 ID。

  4. fileReservedTime=48
    在磁盘上保存消息的时长,单位是小时,自动删除超时的消息。

  5. deleteWhen=04
    与 fileReservedTim巳参数呼应,表明在几点做消息删除动作,默认值 04 表示凌晨 4 点。

  6. brokerRole=SYNC_MASTER
    broker Role 有 3 种: SYNC_MASTER、 ASYNC_MASTER、 SLAVE。 关键 词 SYNC 和 ASYNC 表示 Master 和 Slave 之间同步消息的机制, SYNC 的意思
    是当 Slave 和 Master 消息同步完成后,再返回发送成功的状态。

  7. flushDiskType=ASYNC_FLUSH
    flushDiskType 表示刷盘策略,分为 SYNC_FLUSH 和 ASYNC_FLUSH 两 种,分别代表同步刷盘和异步刷盘。 同步刷盘情况下,消息真正写人磁盘后再返回成功状态;异步刷盘情况下,消息写人 page_cache 后就返回成功状态。

9 ) listenPort=l0911
Broker 监听的端口号,如果一台机器上启动了多个 Broker, 则要设置不同
的端口号,避免冲突。

10 ) storePathRootDir=/home/rocketmq/store- a
存储消息以及一些配置信息的根目录。
这些配置参数,在 Broker 启动的时候生效,如果启动后有更改,要重启 Broker。
现在使用云服务或多网卡的机器比较普遍, Broker 自动探测获得的 ip 地址可能不符合要求,通过 brokerIPl=47 .98.41.234 这样的配置参数,可以设置 Broker 机器对外暴露的 ip 地址。
2.3 发送/接收消息示例
可以用自己熟悉的开发工具创建一个 Java 项目,加人 RocketMQ Client 包 的依赖

发送
创 建一个 DefaultMQProducer 对 象,设置好 GroupName 和 NameServer 地址后启动,然后把待发送的消息拼装成 Message 对象,使 用 Producer 来发送

Consumer 或 Producer 都必须设置 GroupName、 NameServer 地址以及端口
号。 然后指明要操作的 Topic 名称,最后进入发送和接收逻辑。
2.4 常用管理命令
MQAdmin 是 RocketMQ 自带的命令行管理工具,在 bin 目录下,运行 mqadmin 即可执行。 使用 mqadmin 命令,可以进行创建、 修改 Topic,更新 Broker 的配置信息,查询特定消息等各种操作。 本节将介绍几个常用的命令。

  1. 创建/修改 Topic
    消息的发送和接收都要有对应的 Topic, 需要向某个Topic 发送或接收消息,所以在正式使用 RocketMQ 进行消息发送和接收前,要先创建 Topic,创建Topic 的指令是 updateTopic,表2-1列出了支持的参数。

  2. 删除 Topic
    与创建/修改 Topic 对应的是删除 Topic,把 RocketMQ 系 统中不用的 Topic 彻底清除,指令是 deleteTopic, 表 2-2 列出 了支持的参数。

  3. 创建/修改订阅组
    订阅组在提高系统的高可用性和吞吐量方面扮演着重要的角色,比如用 Clustering 模式消费一个 Topic 里的消息内容时,可以启动多个消费者并行消费,每个消费者只消费 Topic 里消息的一部分,以此提高消费速度,这个时候就是通过订阅组来指明哪些消费者是同一组,同一组的消费者共同消费同一个Topic 里的内容。 订阅组可以被自动创建,使用这个命令一般是用来修改订阅组,指令是 updatesubGroup,表 2-3 列出了支持的参数。

  4. 删除订阅组
    与创建或修改订阅组相对应,这个命令删除不再使用的订阅组,指令是 deleteSubGroup,表 2-4 列出了支持的参数。

  5. 更新 Broker 配置
    Broker 有很多 的配置信息,在 Broker 启动时,可以通过配置文件来指定配置信息。 有些配置信息支持在 Broker 运行的时候动态更改,更改指令是 updateBrokerConfig, 表 2-5 列出了支持的参数。

  6. 更新 Topic 的读写权限
    RocketMQ 支持对 Topic 进行权限控制, 主要分为只读的 Topic 和可读写的 Topic,权限可以通过指令 updateTopicPerm 来动态改变,表 2-6 列出了支持的 参数。

  7. 查询 Topic 的路由信息
    Topic 的路由信息指的是某个 Topic 所在的 Broker 相关信息,客户端可以通过 NameServer 来获取这些信息,本命令一般在调试的时候使用,指令是 TopicRoute,表 2-7 列出了支持的参数。

  8. 查看 Topic 列表信息
    上面提到的 TopicRoute 是列出某个 Topic 的相关信息,还有个指令 TopicList
    用来列出集群中所有 Topic 的名称,表 2-8 列出了支持的参数。

  9. 查看 Topic 统计信息
    在使用 RocketMQ 的时候,经常需要查看某个 Topic 的状态,看看消息的
    数量,有多少未处理等, 此时可以通过指令 TopicStats 来查询,表 2-9 列出了 支持的参数。

  10. 根据时间查询消息
    一条消息被发送到 RocketMQ 后 , 默认会带上发送的时间戳, 所以我们可 以根据估计的时间来查询消息,指令是 printMsg,表 2-1 0 列出 了支持的参数。

  11. 根据消息 ID 查询消息
    根据消息 ID 可以精确定位到某条消息,但是消息 ID 需要通过其他方式来 获取, 比如可以先用时间来查询出一些消息,然后定位到要找的具体某个消息, 指令是 queryMsgByld,表 2-1 l 列出了支持的参数。

  12. 查看集群消息
    指令 clusterList 用来列出集群的状态,看看有哪些 Broker 在提供服务 ,
    表 2-12 列出了支持的参数。

2.5 通过图形界面管理集群
运维服务程序是个 SpringBoot 项目,需要从 GitHub 上的 apache/rocketmq
externals 里下载源码 ( https 😕/ github.com/apache/rocketmq-externals/tree/master/
rocketmq-console) 。通过java -jar启动, 在web页面查看
服务启动后,在浏览器里访问server_ip_address:8080 ( server_ip_address是启动rocketmq-console的机器IP)地址就可看到集群的状态。
2.6 本章小结
在生产环境中使用 RocketMQ 集群需要比 QuickStart 部分了解更多的内容, 本章在机器角色、集群配置和部署,以及集群管理方面都做了介绍,用户可以 基于这些内容搭建起一个生成环境的 RocketMQ 消息队列集群,在数据量不大 的非关键场景,可以通过这一章快速上线
第 3 章 用适合的方式发送和接收消息
生产者和消费者是消息队列的两个重要角色,生产者向消息队列写人数据,
消费者从消息队列里读取数据, RocketMQ 的大部分用户只需要和生产者、消
费者打交道。 本章具体介绍不同类型生产者和消费者的特点,以及和它们相关
的 Offset 和 Log。
3.1 不同类型的消费者
根据使用者对读取操作的控制情况,消费者可分为两种类型。 一个是
DefaultMQPushConsumer,由系统控制读取操作,收到消息后自动调用传入的处理方法来处理;另一个是 DefaultMQPullConsumer,读取操作中的大部分功能由使用者自主控制。
3.1.1 DefaultMQPushConsumer 的使用
使用 DefaultMQPushConsumer 主要是设置好各种参数和传人处理消息的函 数。 系统收到消息后自动调用处理函数来处理消息,自动保存 Offset,而且加
入新的 DefaultMQPushConsumer 后会自动做负载均衡。 下面结合 org.apache. rocketmq.example.quickstart 包中的源码来介绍, 如代码清单 3-1 所示。

DefaultMQPushConsumer 需要 设 置三 个 参数 :
一 是这个 Consumer 的 GroupName,
二是 NameServer 的地址和端口号,
三是 Topic 的名称,
下面将分别进行详细介绍。
1 ) Consumer 的 GroupName 用于把多个 Consumer 组织到一起, 提高并发处理能力, GroupName 需要和消息模式 (MessageModel)配合使用。
RocketMQ 支持两种消息模式 : Clustering 和 Broadcasting。
在 Clustering 模式下,同一个 ConsumerGroup ( GroupName 相同 )里的每
个 Consumer 只消费所订阅消息的一部分内容, 同一个 ConsumerGroup 里所有的 Consumer 消费 的内容合起来才是所订阅 Topic 内容的整体,从而达到负载均衡的目的。
在 Broadcasting 模式下,同一个 ConsumerGroup 里的每个 Consumer 都能消费到所订阅 Topic 的全部消息,也就是一个消息会被多次分发,被多个 Consumer 消费。

  1. NameServer 的地址和端口 号,可以填写多个,用分号隔开,达到消除
    单点故障的目的 , 比如 “ip1 :port;ip2:port;ip3 :port” 。

3 ) Topic 名称用来标识消息类型 ,需要提前创建。 如果不需要消费某个 Topic 下的所有消息,可以通过指定消息的 Tag 进行消息过滤,比如:
Consumer. subscribe (”Topic Test”J ’tagl 11 tag2 11 tag3”), 表示这个 Consumer 要 消费“ TopicTest ”下带有 tagl 或 tag2 或 tag3 的消息( Tag 是在发送消息时设置的标签)。 在填写 Tag 参数的位置,用 null 或者“*” 表示要消费这个 Topic的所有消息。
3.1.2 DefaultMQPushConsumer 的处理流程
Push 方式是 Server 端接收到消息后,主动把消息推送给 Client 端,实时性高
Pull 方式是 Client 端循环地从 Server 端拉取消息,主动权在 Client 手里,
自己拉取到一定量消息后,处理妥当了再接着取

从 Broker 的源码中可以看出,服务端接到新消息请求后, 如果队列里没有
新消息,并不急于返回,通过一个循环不断查看状态,每次 waitForRunning
一段 时间 (默认是 5 秒), 然后后 再 Check 默 认情况下当 Broker 一直没
有新 消息, 第三次 Check 的时候, 等待时 间超过 Request 里面的 Broker
SuspendMaxTimeMi11is , 就返回空结果。 在等待的过程中, Broker 收到了新的
消息后会直接调用 notifyMessageArriving 函数返回请求结果。 “长轮询”的核
心是, Broker 端 HOLD 住客户端过来的请求一小段时间,在这个时间内有新
消息到达,就利用现有的连接立刻返回消息给 Consumer。 “长轮询”的主动权还是掌握在 Consumer 手中, Broker 即使有大量消息积压 ,也不会主动推送给Consumer 。

长轮询方式的局限性,是在 HOLD 住 Consumer 请求的时候需要占用资源,
它适合用在消息队列这种客户端连接数可控的场景中。
3.1.3 DefaultMQPushConsumer 的流量控制
见原书
3.1.4 DefaultMQPullConsumer pull
使用 DefaultMQPul!Consumer 像使用 DefaultMQPushConsumer 一样需要 设置各种参数,写处理消息的函数,同时还需要做额外的事情。 接下来结合
org.apache.rocketmq. example.simple 包中的例子源码来介绍,如代码清单 3-7
所示。 见原书

示例代码的处理逻辑是逐个读取某 Topic 下所有 Message Queue 的内容,
读完一遍后退出, 主要处理额外的三件事情:
( 1 )获取 Message Queue 并遍历
一个 Topic 包括多个 Message Queue,如果这个 Consumer 需要获取 Topic
下所有的消息,就要遍历多有的 Message Queue。 如果有特殊情况,也可以选
择某些特定的 Message Queue 来读取消息。
( 2 )维护 Offsetstore
从一个 Message Queue 里拉取消息的时候,要传人 Offset 参数( long 类型 的值),随着不断读取消息 , Offset 会不断增长。 这个时候由用户负责把 Offset
存储下来,根据具体情况可以存到内存里、写到磁盘或者数据库里等。
( 3 )根据不同的消息状态做不同的处理
拉取消息的请求发出后,会返回: FOUND、 NO MATCHED MSG、 NO
NEW MSG、 OFFSET ILLEGAL 四种状态,需要根据每个状态做不同的处理。
比较重要的两个状态是 FOUNT 和 NO NEW MSG,分别表示获取到消息和没
有新的消息。

实际情况中可以把 while (true)放到外层,达到无限循环的目的。 因
为 PullConsumer 需要用户自己处理遍历 Message Queue、保存 Offset,所以
Pull Consumer 有更多的自主性和灵活性。
3.1.5 Consumer 的启动、关闭流程
消息队列一般是提供一个不间断的持续性服务, Consumer 在使用过程中, 如何才能优雅地启动和关闭,确保不漏掉或者重复消费消息呢?

Consumer 分为 Push 和l Pull 两种方式,对于 PullConsumer 来说,使用 者主动权很高,可以根据实际需要暂停、停止、启动消费过程。 需要注意的是
Offset 的保存,要在程序的异常处理部分增加把 Offset 写人磁盘方面的处理,记准了每个 Message Queue 的 Offset,才能保证消息消费的准确性。

DefaultMQPushConsumer 的退出, 要调用 shutdown() 函数, 以便 释放资
源、保存 Offset 等。 这个调用要加到 Consumer 所在应用的退出逻辑中。

Push Consumer 在启动的时候,会做各种配置检查,然后连接 NameServer
获取 Topic 信息,启动时如果遇到异常,比如无法连接 NameServer,程序仍然
可以正常启动不报错(日志里有 WARN 信息)。 在单机环境下可以测试这种情
况,启动 DefaultMQPushConsumer 时故意把 NameServer 地址填错,程序仍然
可以正常启动,但是不会收到消息。

为什么 DefaultMQPushConsumer 在无法连接 NameServer 时不直接报错退
出呢? 这和分布式系统的设计有关, RocketMQ 集群可以有多个 NameServer、
Broker,某个机器出异常后整体服务依然可用。 所以 DefaultMQPushConsumer
被设计成当发现某个连接异常时不立刻退出,而是不断尝试重新连接。 可以进
行这样一个测试,在 DefaultMQPushConsumer 正常运行的时候,手动 kill 掉
Broker 或 NameServer,过一会儿再启动。 会发现 DefaultMQPushConsumer 不
会出错退出,在服务恢复后正常运行,在服务不可用的这段时间 ,仅仅会在日
志里报异常信息。

如果需要在 DefaultMQPush Consumer 启动的时候,及时暴露配置问题,该
如何操作呢? 可以在 Consumer.start()语句后调用: Consumer.fetchSubscribeMe
ssageQueues(”TopicName”),这时如果配置信息写得不准确,或者当前服务不可 用,这个语句会报 MQC!ientException 异常。
3.2 不同类型的生产者
生产者向消息队列里写人消息,不同的业务场景需要生产者采用不同的写人
策略。 比如同步发送、异步发送、 延迟发送、 发送事务消息等, 下面具体介绍。
3.2.1 DefaultMQProducer
生产者发送消息默认使用的是 DefaultM Q Producer 类, 下面结合实际代码 来详细解释,如代码清单 3-8 所示。

发送消息要经过五个步骤 :
1 )设置 Producer 的 GroupName。
2 )设置 lnstanceName ,当一个 Jvm 需要启动多个 Producer 的时候,通过 设置不同的 InstanceName 来区分,不设置的话系统使用默认名称“DEFAULT” 。
3 )设置发送失败重试次数,当网络出现异常的时候,这个次数影响消息的 重复投递次数。 想保证不丢消息,可以设置多重试几次。
4 )设置 NameServer 地址。
5 )组装消息并发送。

消息的发送有同步和异步两种方式,上面的代码使用的是异步方式。

在 第 2 章的例子中用的是同步方式。 消息发送的返回状态有如下四种 :
FLUSH DISK TIMEOUT 、
FLUSH SLAVE TIMEOUT 、
SLAVE NOT AVAILABLE 、
SEND OK,
不同状态在不同的刷盘策略和同步策略的配置下含义是不同的。

FLUSH_DISK_TIMEOUT : 表示没有在规定时间内完成刷盘(需要 Broker 的刷盘策略被设置成 SYNC_FLUSH 才会报这个错误)。
FLUSH_SLAVE_TIMEOUT :表示在主备方式下,并且 Broker 被设置 成 SYNC_MASTER 方式,没有在设定时间内完成主从同步。
SLAVE_NOT_AVAILABLE : 这个状态产生的场景和 FLUSH SLAVE TIMEOUT 类似, 表示在主备方式下,并且 Broker 被设置成 SYNC_MASTER,但是没有找到被配置成 Slave 的 Broker。
SEND_OK :表示发送成功,发送成功的具体含义,比如消息是否已经 被存储到融盘?消息是否被同步到了 Slave 上?消息在 Slave 上是否被 写人磁盘?需要结合所配置的刷盘策略、主从策略来定。 这个状态还可 以简单理解为,没有发生上面列出的三个问题状态就是 SEND OK。

写一个高质量的生产者程序,重点在于对发送结果的处理,要充分考虑各
种异常,写清对应的处理逻辑。
3.2.2 发送延迟消息
RocketMQ 支持发送延迟消息, Broker 收到这类消息后 ,延迟一段时间再 处理, 使消息在规定的一段时间后生效。
延迟消息的使用方法是在创建 Message 对象时,调用 setDelayTimeLevel ( int level) 方法设置延迟时间, 然后再把这个消息发送出去。 目前延迟的时间不支 持任意设置,仅支持预设值的时间长度 ( 1 s/5s/1 Os/30s/I m/2m/3m/4m/5m/6m/
7m/8m/9m/1 Om/20m/30m/1 h/2h) 。
比如 setDelayTimeLevel(3) 表示延迟 10s。
3.2.3 自定义消息发送规则
一个 Topic 会有多个 Message Queue,如果使用 Producer 的默认配置,这 个 Producer 会轮流向各个 Message Queue 发送消息。 Consumer 在消费消息的 时候,会根据负载均衡策略,消费被分配到的 Message Queue,如果不经过特 定的设置,某条消息被发往l哪个 Message Queue,被哪个 Consumer 消费是未 知的。

如果业务需要我们把消息发送到指定的 Message Queue 里,比如把同一 类型 的消息都发往相同的 Message Queue, 该怎 么办呢? 可以用 MessageQueueSelector, 如代码清单 3-9 所示。

发送消息的时候,把 MessageQueueSelector 的对象作为参数,使用 public
SendResult send ( Message msg, MessageQueueSelector selector, Object arg)函
数发送消 息即可。 在 MessageQueueSelector 的实现中,根据传人的 Object 参
数,或者根据 Message 消息内容确定把消息发往那个 Message Queue,返回被
选中的 Message Queue。
3.2.4 对事务的支持
RocketMQ 采用两阶段提交的方式实现事务消息,
3.3 如何存储队列位置信息
RocketMQ中,一种类型的消息会放到一个Topic里,为了能够并行,一般一个Topic会有多个Message Queue (也可以设置成一个),Offset是指某个Topic下的一条消息在某个Message Queue里的位置,通过Offset的值可以定位到这条消息,

Offset 的类结构,主要分为本地文件类型和 Broker 代存的类型两种
对于 DefaultMQPushConsurner 来说,默认是 CLUSTERING 式,也就是同一个 Consumer group 里的多个消费者每人消费一部分,各自收到 的消息内容不一样 这种情况下,由 Broker 端存储和控制 Offset 的值,使用 RemoteBrokerOffsetStore 结构

如果 Pull Consumer ,就要自己处理 OffsetStore
把 Offset 存到了内存,没有持久化存储,这样就 可能因为序的异常或启而丢失 Offset ,

了解OffsetStore 储机制以后,我们看看如何设置 Consumer 读取消的初始位置

3.4 自定义日志输出
Log 是监控系统状态问题的重要手段
RocketMQ Log 存储 位置是:$ {user.home }/Logs/rocketmqLogs,
Log配置文件的设置可以通过 JVM 启动参数、环境变量、代码中的设语句这三种方式
3.5 本章小结
对消息队列使用者来说,Consumer 和Producer是打交道最多的两个类型。本章详细介绍了两种类型的Consumer 和一种类型的 Producer,用户在使用的时候基于业务需求来选择合适的类型。最后重点介绍了Offset和 Log,了解Offset机制是正确使用RocketMQ的基础,合理使用Log可以大幅提高开发、调试的效率。下一章将介绍RocketMQ 的NameServer模块。
第4章 分布式消息队列的协调者
对于一个消息队列集群来说,系统由很多台机器组成,每个机器的角色、 IP地址都不相同,而且这些信息是变动的 这种情况下, 如果一个新的Producer Consumer 加入 ,怎么配置连接信息呢 NameServer 的存在主要是为了解决这类问题,由 NameServer 维护这些配置信息 状态信息,其他角色都通过 NameServer 来协同执行

4.1 NameServer的功能
NameServer是整个消息队列中的状态服务器,集群的各个组件通过它来了解全局的信息。同时,各个角色的机器都要定期向NameServer 上报自己的状态,超时不上报的话,NameServer 会认为某个机器出故障不可用了,其他的组件会把这个机器从可用列表里移除。
NamServer可以部署多个,相互之间独立,其他角色同时向多个NameServer机器上报状态信息,从而达到热备份的目的。NameServer本身是无状态的,也就是说NameServer 中的 Broker、Topic等状态信息不会持久存储,都是由各个角色定时上报并存储到内存中的(NameServer支持配置参数的持久化,一般用不到)。
4.1.1 集群状态的存储结构
org.apache.rocketmq.namesrv.routeinfo RoutelnfoManager 中,集群的状态就保存在这五个变量中
从上面这五个变 的定义,可以清楚地 出各个组件的状态是如何存储的, NameServer 的主要工作就是维护这五个变量中存储的信息
4.1.2 状态维护逻辑
4.2 各个角色间的交互流程
4.2.1 交互流程源码分析
4.3 底层通信机制
4.3.1 Remoting 模块
RocketMQ 的通信相 代码在 Remoting 模块里
4.3.2 协议设计和编解码
RocketMQ 自己定义了一个通信协议,使得模块间传输的二进制消息和有 意义的内容之 互相转换协议
4.3.3 Netty库
RocketMQ是基于Netty库来完成RemotingServer 和 RemotingClient具体的通信实现的,Netty是个事件驱动的网络编程框架,它屏蔽了Java Socket、NIO等复杂细节,用户只需用好Netty,就可以实现一个“网络编程专家+并发编程专家”水平的Server、Client网络程序。应用Netty有一定的门槛
4.4本章小结
本章介绍了NameServer的功能,NameServer在 RocketMQ集群中扮演调度中心的角色。各个Producer、Consumer上报自己的状态上去,同时从NameServer获取其他角色的状态信息。NameServer的功能虽然非常重要,但是被设计得很轻量级,代码量少并且几乎无磁盘存储,所有的功能都通过内存高效完成。本章还介绍了底层的通信机制,RocketMQ基于Netty对底层通信做了很好的抽象,使得通信功能逻辑清晰,代码简单。Netty 的介绍和具体的通信实现可以查看第13章。
第5章 消息队列的核心机制
Broker RocketMQ 的核心,大部分‘重量级”工作都是由 Broker 完成的,
包括接收 Producer 发过来的消息、处理 Consumer 的消费消息请求、-消息的持
久化存储、消息的 HA 机制以及服务端过滤功能等
5.1 消息存储和发送
使用磁盘顺序写和
使用“零拷贝”技术,提高消息存 盘和网络发送的速度
5.2 消息存储结构
RocketMQ 消息的存储是由 ConsumeQueue CommitLog 配合完成的,消息真正的物理存储文件是 CommitLog, ConsumeQueue 是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址

采取一些机制,尽量向CommitLog中顺序写,但是随机读。
ConsumeQueue 的内容也会被写到磁盘里作持久存储。

存储机制这样设计有以下几个好处:
1 )CommitLog顺序写,可以大大提高写入效率。
2)虽然是随机读,但是利用操作系统的pagecache机制,可以批量地从磁盘读取,作为cache存到内存中,加速后续的读取速度。
3 )为了保证完全的顺序写,需要 ConsumeQueue 这个中间结构 ,
5.3 高可用性机制
RocketMQ 分布式集群是通过 Master Slave 的配合达到高可用性的,首 先说一下 Master Slave 的区别:在 Broker 的配 文件中,参数 brokerId 的值为0表明这个Broker是 Master,大于0表明这个Broker是Slave,同时brokerRole参数也会说明这个Broker是 Master还是Slave。Master 角色的 Broker支持读和写,Slave角色的Broker仅支持读,也就是Producer 只能和 Master角色的 Broker连接写入消息;Consumer可以连接Master角色的Broker,也可以连接Slave角色的 Broker来读取消息。

在Consumer 的配置文件中,并不需要设置是从 Master读还是从Slave读,当Master不可用或者繁忙的时候,Consumer会被自动切换到从 Slave读。有了自动切换Consumer这种机制,当一个Master角色的机器出现故障后,Consumer仍然可以从Slave读取消息,不影响Consumer程序。这就达到了消费端的高可用性。

如何达到发送端的高可用性呢?在创建Topic的时候,把Topic的多个Message Queue创建在多个Broker组上(相同 Broker名称,不同brokerId的机器组成一个 Broker组),这样当一个 Broker组的 Master不可用后,其他组的Master仍然可用,Producer仍然可以发送消息。RocketMQ目前还不支持把Slave自动转成Master,如果机器资源不足,需要把Slave转成 Master,则要手动停止 Slave角色的Broker,更改配置文件,用新的配置文件启动Broker,
5.4同步刷盘和异步刷盘
异步刷盘方式:在返回写成功状态时,消息可能只是被写入了内存的PAGECACHE,写操作的返回快,吞吐量大;当内存里的消息量积累到一定程度时,统一触发写磁盘动作,快速写入。

同步刷盘方式:在返回写成功状态时,消息已经被写入磁盘。具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息写成功的状态。

同步刷盘还是异步刷盘,是通过 Broker 配置文件里的 flushDiskType 参数
设置的,这个参数被配置成 SYNC_FLUSH ASYNC_FLUSH 中的一个
5.5 同步复制和异步复制
如果一个 Broker组有Master 和 Slave,消息需要从 Master复制到Slave上,有同步和异步两种复制方式。同步复制方式是等Master和 Slave均写成功后才反馈给客户端写成功状态;异步复制方式是只要Master写成功即可反馈给客户端写成功状态。

这两种复制方式各有优劣,在异步复制方式下,系统拥有较低的延迟和较高的吞吐量,但是如果 Master 出了故障,有些数据因为没有被写入Slave,有可能会丢失;在同步复制方式下,如果 Master 出故障,Slave上有全部的备份数据,容易恢复,但是同步复制会增大数据写人延迟,降低系统吞吐量。

同步复制和异步复制是通过Broker配置文件里的 brokerRole参数进行设置的,这个参数可以被设置成ASYNC_MASTER、SYNC_MASTER、SLAVE三个值中的一个。

实际应用中要结合业务场景,合理设置刷盘方式和主从复制方式,尤其是SYNC_FLUSH方式,由于频繁地触发磁盘写动作,会明显降低性能。通常情况下,应该把 Master和 Save配置成ASYNC_FLUSH的刷盘方式,主从之间配置成SYNC_MASTER的复制方式,这样即使有一台机器出故障,仍然能保证数据不丢,是个不错的选择。
5.6本章小结
本章介绍了RocketMQ消息队列实现的难点及核心,即“队列”本身的实现,基于磁盘做一个读写效率高的队列并非易事,实现不好就会使磁盘操作成为整个系统的瓶颈,无法提升系统的吞吐量。RocketMQ基于“顺序写”“随机读”的原则来设计,利用“零拷贝”技术,克服了磁盘操作的瓶颈。

另一个难点是为了高可用性而设计的主从机制,数据被及时复制到多个机器,这样当一台机器出故障后,整体系统依然可用。这样可靠性和性能能直接有个权衡,RocketMQ把选择权留给用户,用户根据具体的业务场景来选择要更高的可靠性,还是要更高的效率。
第6章 可靠性优先的使用场景
本章的重点是可靠性,解决如何让消息队列满足业务逻辑需求,同时稳定可靠地长期运行。
6.1顺序消息
顺序消息是指消息的消费顺序和产生顺序相同,
顺序消息分为全局顺序消息和部分顺序消息,
全局顺序消息指某个Topic下的所有消息都要保证顺序;
部分顺序消息只要保证每一组消息被顺序消费即可
6.1.1 全局顺序消息
RocketMQ在默认情况下不保证顺序,比如创建一个Topic,默认八个写队列,八个读队列。这时候一条消息可能被写入任意一个队列里;在数据的读取过程中,可能有多个Consumer,每个Consumer也可能启动多个线程并行处理,所以消息被哪个Consumer消费,被消费的顺序和写入的顺序是否一致是不确定的。

要保证全局顺序消息,需要先把Topic的读写队列数设置为一,然后Producer和 Consumer的并发设置也要是一。简单来说,为了保证整个Topic 的全局消息有序,只能消除所有的并发处理,各部分都设置成单线程处理。这时高并发、高吞吐量的功能完全用不上了。
在实际应用中,更多的是像订单类消息那样,只需要部分有序即可。在这种情况下,我们经过合适的配置,依然可以利用RocketMQ高并发、高吞吐量的能力。
6.1.2部分顺序消息
要保证部分消息有序,需要发送端和消费端配合处理。在发送端,要做到把同一业务ID的消息发送到同一个Message Queue ;在消费过程中,要做到从同一个Message Queue读取的消息不被并发处理,这样才能达到部分有序。

发送端使用MessageQueueSelector类来控制把消息发往哪个MessageQueue
消费端通过使用MessageListenerOrderly类来解决单Message Queue 的消息被并发处理的问题
6.2 消息重复问题
RocketMQ选择了确保一定投递,保证消息不丢失,但有可能造成消息重复。
解决消息重复有两种方法:
第一种方法是保证消费逻辑的幂等性(多次调用和一次调用效果相同);
另一种方法是维护一个已消费消息的记录,消费前查询这个消息是否被消费过。这两种方法都需要使用者自己实现。
6.3 动态增减机器
6.3.1 动态增减 NameServer
6.3.2 动态、增减 Broker
6.4各种故障对消息的影响
我们期望消息队列集群一直可靠稳定地运行,但有时候故障是难免的,本节我们列出可能的故障情况,看看如何处理:
1 ) Broker正常关闭,启动;
2 ) Broker异常Crash,然后启动;
3 ) oS Crash,重启﹔
4)机器断电,但能马上恢复供电;
5)磁盘损坏;
6 ) CPU、主板、内存等关键设备损坏。
假设现有的RocketMQ集群,每个Topic都配有多Master 角色的Broker供写入,并且每个Master都至少有一个Slave机器(用两台物理机就可以实现上述配置),我们来看看在上述情况下消息的可靠性情况。

第1种情况属于可控的软件问题,内存中的数据不会丢失。如果重启过程中有持续运行的Consumer,Master机器出故障后,Consumer会自动重连到对应的Slave机器,不会有消息丢失和偏差。当Master角色的机器重启以后,Consumer又会重新连接到Master机器(注意在启动Master机器的时候,如果Consumer正在从Slave消费消息,不要停止Consumer。假如此时先停止Consumer后再启动Master机器,然后再启动Consumer,这个时候Consumer就会去读Master机器上已经滞后的offset值,造成消息大量重复)。
如果第1种情况出现时有持续运行的 Producer,一台Master出故障后,Producer 只能向Topic下其他的Master机器发送消息,如果 Producer采用同步发送方式,不会有消息丢失。
第2、3、4种情况属于软件故障,内存的数据可能丢失,所以刷盘策略不同,造成的影响也不同,如果 Master、Slave都配置成SYNC_FLUSH,可以达到和第1种情况相同的效果。

第5、6种情况属于硬件故障,发生第5、6种情况的故障,原有机器的磁盘数据可能会丢失。如果 Master和 Slave机器间配置成同步复制方式,某一台机器发生5或6的故障,也可以达到消息不丢失的效果。如果 Master和 Slave机器间是异步复制,两次 Sync间的消息会丢失。
总的来说,当设置成:
1)多Master,每个Master带有Slave;
2)主从之间设置成SYNC_MASTER;
3 ) Producer 用同步方式写;
4)刷盘策略设置成SYNC_FLUSH。
就可以消除单点依赖,即使某台机器出现极端故障也不会丢消息
6.5 消息优先级
RocketMQ 是个先人先出的队列,不支持消息级别或者 Topic 级别的优先级 业务中简单的优先级需求,可以通过间接的方式解决,下面列举三种优先级相关需求的具体处理方法
6.6 本章小结
本章根据使用场景,讨论如何“可靠”地收发消息。即在要求消息顺序的场景下,如何既能并发执行,又能保证消息顺序;然后分析在可能的故障场景下,如何应对以保证不丢消息、不中断服务。RocketMQ在设计上,有重试机制来保证消息不丢,造成的结果是可能存在消息重复,这一点需要用户根据具体业务场景来处理。下一章将讨论处理大数据量消息的方法。
第7章 吞吐量优先的使用场景
本章介绍在大流量场景下,提高 RocketMQ 集群吞吐量的一些方法,有些方法当服务器出异常时会增大丢消息的概率,用户需要根据业务需求酌情使用
7.1 在Broker端进行消息过滤
在 Broker端进行消息过滤,可以减少无效消息发送到Consumer,少占用网络带宽从而提高吞吐量。Broker端有三种方式进行消息过滤。
7.1.1 消息的Tag和Key
对一个应用来说,尽可能只用一个Topic,不同的消息子类型用Tag来标识(每条消息只能有一个Tag),服务器端基于Tag进行过滤,并不需要读取消息体的内容,所以效率很高。发送消息设置了Tag以后,消费方在订阅消息时,才可以利用Tag在 Broker端做消息过滤。

其次是消息的Key。对发送的消息设置好Key,以后可以根据这个Key来查找消息。所以这个Key一般用消息在业务层面的唯一标识码来表示,这样后续查询消息异常,消息丢失等都很方便。Broker 会创建专门的索引文件,来存储Key到消息的映射,由于是哈希索引,应尽量使Key唯一,避免潜在的哈希冲突。

Tag和 Key的主要差别是使用场景不同,Tag用在Consumer的代码中,用来进行服务端消息过滤,Key主要用于通过命令行查询消息。
7.1.2 通过 Tag 进行过滤
用Tag方式进行过滤的方法是传入感兴趣的Tag标签,Tag标签是一个普通字符串,是在创建Message的时候添加的,一个 Message只能有一个Tago使用Tag方式过滤非常高效,Broker端可以在ConsumeQueue中做这种过滤,只从 CommitLog里读取过滤后被命中的消息。
7.1.3 SQL 表达式的方式进行过滤
用类似 SQL 达式 的方式对消息进行过滤
SQL表达式方式的过滤需Broker先读出消息的属性内容,后做SQL计算增大盘压力没有Tag 方式高效
7.1.4 Filter Server 方式过滤
Filter Server 种比 SQL 达式更灵活的过滤方式,允许用户自定义Java 函数,根据 Java 逻辑对消息进行过滤
7.2 提高 Consumer 处理能力
当Consumer的处理速度跟不上消息的产生速度,会造成越来越多的消息积压,这个时候首先查看消费逻辑本身有没有优化空间,除此之外还有三种方法可以提高Consumer 的处理能力。
(1)提高消费并行度
在同一个ConsumerGroup 下 ( Clustering方式),可以通过增加Consumer实例的数量来提高并行度,通过加机器,或者在已有机器中启动多个Consumer进程都可以增加 Consumer实例数。注意总的Consumer数量不要超过Topic下Read Queue数量,超过的Consumer实例接收不到消息。此外,通过提高单个Consumer实例中的并行处理的线程数,可以在同一个 Consumer 内增加并行度来提高吞吐量(设置方法是修改consumeThreadMin和 consumeThreadMax)。

(2)以批量方式进行消费
某些业务场景下,多条消息同时处理的时间会大大小于逐个处理的时间总和,比如消费消息中涉及update某个数据库,一次 update10条的时间会大大小于十次update1条数据的时间。这时可以通过批量方式消费来提高消费的吞吐量。实现方法是设置Consumer 的 consumeMessageBatchMaxSize这个参数,默认是1,如果设置为N,在消息多的时候每次收到的是个长度为N的消息链表。

(3)检测延时情况,跳过非重要消息
Consumer在消费的过程中,如果发现由于某种原因发生严重的消息堆积,短时间无法消除堆积,这个时候可以选择丢弃不重要的消息,使Consumer尽快追上 Producer的进度
7.3 Consumer的负载均衡
想要提高Consumer的处理速度,可以启动多个Consumer并发处理,这个时候就涉及如何在多个Consumer之间负载均衡的问题

在RocketMQ 中,负载均衡或者消息分配是在Consumer端代码中完成的,Consumer 从 Broker处获得全局信息,然后自己做负载均衡,只处理分给自己的那部分消息。

7.3.1 DefaultMQPushConsumer的负载均衡
DefaultMQPushConsumer的负载均衡过程不需要使用者操心,客户端程序会自动处理,每个.DefaultMQPushConsumer启动后,会马上会触发一个doRebalance动作;而且在同一个ConsumerGroup里加入新的 DefaultMQPush-Consumer时,各个Consumer都会被触发doRebalance动作。

具体的负载均衡算法有五种,默认用的是第一种AllocateMessageQueueAveragely。负载均衡的结果与Topic的 Message Queue数量,以及 ConsumerGroup里的Consumer的数量有关。负载均衡的分配粒度只到Message Queue,把 Topic下的所有Message Queue分配到不同的Consumer 中,所以Message Queue和 Consumer 的数量关系,或者整除关系影响负载均衡结果。

图7-2 RocketMQ 客户端负载均衡策略
AllocateMessageQueueAveragely
AllocateMessageQueueAveragelyByCircle
AllocateMessageQueueByConfig
AllocateMessageQueueByMachineRoom
AllocateMessageQueueConsistentHash

以AllocateMessageQueueAveragely策略为例,如果创建Topic的时候,把Message Queue数设为3,当Consumer数量为2的时候,有一个 Consumer需要处理Topic三分之二的消息,另一个处理三分之一的消息;当Consumer数量为4的时候,有一个Consumer无法收到消息,其他3个Consumer各处理Topic 三分之一的消息。可见Message Queue数量设置过小不利于做负载均衡,通常情况下,应把一个Topic的 Message Queue数设置为16。

7.3.2 DefaultMQPullConsumer 的负载均衡
Pull Consumer 可以看到所有的 Message Queue 而且从哪个 Message Queue 读取消息,读消息时的 Offset 都由使用者控制,使用者可以实现任何特殊方式的负载均衡。

DefaultM Q Pull Consumer 有两个辅助方法可以帮助实现负载均衡,
一个是 registerMessageQueueListener 函数
用户通过更改 MessageQueueListenerimpl 现来做自己 的负载均衡策略
7.4提高Producer的发送速度
发送一条消息出去要经过三步,一是客户端发送请求到服务器,二是服务器处理该请求,三是服务器向客户端返回应答,一次消息的发送耗时是上述三个步骤的总和。在一些对速度要求高,但是可靠性要求不高的场景下,比如日志收集类应用,可以采用Oneway方式发送,Oneway方式只发送请求不等待应答,即将数据写入客户端的Socket缓冲区就返回,不等待对方返回结果,用这种方式发送消息的耗时可以缩短到微秒级。

另一种提高发送速度的方法是增加 Producer的并发量,使用多个Producer同时发送,我们不用担心多Producer同时写会降低消息写磁盘的效率,RocketMQ引入了一个并发窗口,在窗口内消息可以并发地写入DirectMem中,然后异步地将连续一段无空洞的数据刷入文件系统当中。顺序写CommitLog可让RocketMQ无论在 HDD还是SSD磁盘情况下都能保持较高的写入性能
7.5 系统性能调优的一般流程
7.6本章小结
本章重点关注性能,关注在大消息量的情况下,如何提高RocketMQ的吞吐量。首先介绍了消息过滤,在服务端进行消息过滤可以减少无效消息传输造成的带宽浪费,Tag是最常用的一种高效过滤方式,此外还可以用SQL表达式、FilterServer来过滤消息。
另一个提高吞吐量的方法是增加集群的机器数量,提高并发性,要根据实际场景增加 Broker、Consumer或Producer角色的机器数量。
第8章 和其他系统交互
Springboot的方式使用rocketmq
通过Spring Messaging方式使用, springboot提供的消息统一抽象
直接使用云上RocketMQ Paas
与Spark, flink对接
8.4自定义开发运维工具
8.4.1 开源版本运维工具功能介绍
第1章介绍过如何启动运维页面,运维页面打开后,从左至右有7个Tab,分别是:配置、驾驶舱、集群信息、Topic信息、Consumer信息Producer信息和消息查询

首先在配置页面,设置好NaveServer的地址

在驾驶舱中可以查看Broker 的消息量(总量/5分钟图),还可以查看单主题的消息量(总量/趋势图)。

在集群信息页面,可以查看集群数量、地址、主从的分布情况,还可以查看Broker的运行状态信息和配置信息。

Topic页面展示所有的主题,可以通过搜索框进行过滤,筛选普通/重试死信类型的主题;还可以添加/更新主题,修改主题的配置参数。每个参数的含义和 MQAdmin命令中updateTopc命令的参数对应。还可以查看每个主题的消息投递状态,消息的路由信息(这个主题的消息会发往哪些Broker,对应Broker 的 Message Queue信息)。还可以向某个主题发送测试消息和重置消费位点(Offset)。

Consumer信息页面展示所有的消费组,还可以通过搜索框进行搜索,手动刷新页面或每隔五秒定时刷新页面,按照订阅组/数量/TPS/延迟进行排序添加/更新消费组等。

Producer信息页面,可以通过Topic和 Group查询在线的消息生产者信息信息包含客户端的主机、版本等。

消息查询页面,可以根据Topic的时间、Key和消息ID进行消息查询。消息详情可以展示这条消息的详细内容。消息详情可以查看消息对应的具体消费组的消费情况(如果异常,可以查看具体的异常信息)。可以向指定的消费组重发消息。
8.4.2基于Tools模块开发自定义运维工具
第9章 首个Apache中间件顶级项目
9.3 源码结构
RocketMQ的源码结构如图9-2所示,整个项目是用Maven来管理的,共有十几个模块,主要功能通过broker、client、common、namesrv、 remotingstore、tools这几个模块实现。
namesrv、broker、client这三个模块前文都有介绍,namesrv是分布式队列集群的协调者,broker实现了消息队列的主体,client包括生产者和消费者,包括使用消息队列的很多辅助方式。common模块包括一些公共的功能类实现,remoting是通信相关功能的实现,store是消息存储的实现,tools主要是管理工具,用来管理集群。
第10章 NameServer 源码解析
10.2NameServer的总控逻辑
NameServer的总控逻辑在NamesrvController.java代码中。NameServer是集群的协调者,它只是简单地接收其他角色报上来的状态,然后根据请求返回相应的状态。首先,NameserverController把执行线程池初始化好

启动了一个默认是8个线程的线程池( private int serverWorkerThreads = 8 ),还有两个定时执行的线程,一个用来扫描失效的Broker(scanNotActiveBroker ),另一个用来打印配置信息(printAllPeriodically)。

然后启动负责通信的服务remotingServer,remotingServer监听一些端口,收到 Broker、Client等发过来的请求后,根据请求的命令,调用不同的Processor来处理。这些不同的处理逻辑被放到上面初始化的线程池中执行.

10.4集群状态存储
NameServer作为集群的协调者,需要保存和维护集群的各种元数据,这是通过RouteInfoManager类来实现的
第11章 最常用的消费类
编写程序消费RocketMQ中消息的时候,最常用的类是DefaultMQPushConsumer,这个类让我们消费消息变得很简单,这个类到底默默地为我们做哪些事情呢?本章将对其做详细分析。
11.2消息的并发处理
本节重点看一下实现消息并发处理的代码,并发处理会增大实现流量控制保证消息顺序方面的难度。
11.2.1 并发处理过程
处理效率的高低是反应Consumer实现好坏的重要指标,本节以 Consume-MessageConcurrentlyService类为例来分析RocketMQ的实现方式。ConsumeMessageConcurrentlyService类在org.apache.rocketmq.client.impl.consumer包中
这个类定义了三个线程池,一个主线程池用来正常执行收到的消息,用户可以自定义通过consumeThreadMin和 consumeThreadMax来自定义线程个数另外两个都是单线程的线程池,一个用来执行推迟消费的消息,另一个用来定期清理超时消息( 15分钟)
11.3生产者消费者的底层类
无论是生产者还是消费者,在底层都要和 Broker打交道,进行消息收发。在源码层面,底层的功能被抽象成同一个类,负责和 Broker打交道,本节详细介绍这个类的情况。
11.4本章小结
本章分析的是Client模块里的代码,我们在使用RocketMQ的时候,更多的是和这个模块里的代码打交道。本章重点分析了DefaultMQPushConsumerImpl类,然后分析了Consumer的并发处理过程,最后分析了客户端Class统一的底层通信类MQClientInstance。下一章将从代码层面分析RocketMQ的主从同步机制。
第12章 主从同步机制
RocketMQ的 Broker分为Master和 Slave两个角色,为了保证高可用性Master角色的机器接收到消息后,要把内容同步到Slave机器上,这样一旦Master宕机,Slave机器依然可以提供服务。本章分析Master和 Slave角色机器间同步功能实现的源码。

12.1同步属性信息
Slave需要和 Master同步的不只是消息本身,一些元数据信息也需要同步,比如TopicConfig信息、ConsumerOffset信息、DelayOffset和SubscriptionGroupConfig信息。Broker在启动的时候,判断自己的角色是否是Slave,是的话就启动定时同步任务
12.2同步消息体
本节介绍Master和 Slave之间同步消息体内容的方法,也就是同步CommitLog内容的方法
12.3 sync_master和 async master
sync_master和 async_master是写在 Broker配置文件里的配置参数,这参数影响的是主从同步的方式。从字面意思理解,sync_master是同步方式,也就是Master角色 Broker中的消息要立刻同步过去; async_master是异步方式也就是Master角色Broker 中的消息是通过异步处理的方式同步到Slave角色机器上的。
12.4本章小结
本章分析了Master和 Slave角色的 Broker 之间同步信息功能的实现。需要同步的信息分为两种类型,实现方式各不相同:一种是元数据信息,采用基于Netty的 command方式来同步消息;另一种是commitLog 信息,同步方式是直接基于Java NIO来实现。下一章将介绍RocketMQ底层通信逻辑的具体实现。
第13章 基于Netty的通信实现
本章分析RocketMQ底层通信的实现机制,作为一个分布式消息队列,通信的质量至关重要。基于TCP协议和Socket实现一个高效、稳定的通信程序并不容易,有很多大大小小的“坑”等待着经验不足的开发者。RocketMQ选择不重复发明轮子,基于Netty库来实现底层的通信功能。
13.1 Netty介绍
Netty是一个网络应用框架,或者说是一个Java网络开发库。Netty提供异步事件驱动的方式,使用它可以快速地开发出高性能的网络应用程序,比如客户端/服务器自定义协议程序,大大简化了网络程序的开发过程。

Netty是一个精心设计的框架,它从许多协议实现中吸收了丰富的经验,比如FTP、SMTP、HTTP等许多基于二进制和文本的传统协议。借助Netty,可以比较容易地开发出达到Java网络专家+并发编程专家水平的通信程序。

了解 Netty前需要对Java NIO有个基本的了解,熟悉Channel、ByteBuffer、Selector等基本概念。
13.2 Netty架构总览
Netty主要分为三部分:
一是底层的零拷贝技术和统一通信模型;
二是基于JVM实现的传输层;
三是常用协议支持。
13.2.1 重新实现ByteBuffer
在网络通信中,CPU处理数据的速度大大快于网络传输数据的速度,所以需要引人缓冲区,将网络传输的数据放入缓冲区,累积足够的数据再发给CPU处理。
13.2.2 统一的异步I/O接口
13.2.3 基于拦截链模式的事件模型
13.2.4 高级组件
13.3 Netty 用法示例
13.3.2 查看收到的数据
13.4 RocketMQ基于Netty的通信功能实现
RocketMQ底层通信的实现是在Remoting模块里,因为借助了Netty,RocketMQ的通信部分没有很多的代码,就是用Netty实现了一个自定义协议的客户端/服务器程序。
13.4.1 顶层抽象类
13.4.2 自定义协议
无论是服务端还是客户端都需要处理接收到的请求,处理方法由
processRequestCommand 定义,注意这里接收到的消息已经被转换成 Remoting
Command 了,而不是原始的字节流。
RemotingCommand 是 RocketMQ 自定义的协议,
13.4.3 基于Netty的Server和Client
13.5 本章小结
本章介绍了RocketMQ底层通信的实现机制,由于它是基于Netty来实现的,所以首先介绍了Netty的基础知识。Netty被用在很多开源软件的底层通信部分,RocketMQ以Netty为基础,还实现了一种机制,把通信功能和消息处理功能分离,不同类型的通信内容被抽象成发送带有对应类型代码的Command,同时根据类型代码查找对应的Processor和 Executor来执行,结构非常清晰,为我们自己实现网络通信程序提供了参考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值