1. 基于公司内部开源共建原则, RocketMQ项目只维护核心功能,且去除了所有其他运行时依赖,核心功能最简化。每个BU的个性化需求都在RocketMQ项目之上进行深度定制。RocketMQ向
其他BU提供的仅仅是Jar包,例如要定制一个Broker,那么只需要依赖rocketmq-broker这个jar包即可,可通过API进行交互,如果定制client,则依赖rocketmq-client这个jar包,对其提供的api进行再封装。
2. 广播消费:
一条消息被多个Consumer消费,即使这些Consumer属于同一个Consumer Group,消息也会被Consumer Group中的每个Consumer都消费一次,广播消费中的Consumer Group概念可以认为在消息划分方面无意义。
集群消费:
一个Consumer Group中的Consumer实例平均分摊消费消息。例如某个Topic有9条消息,其中一个Consumer Group有3个实例(可能是3个进程,或者3台机器),那么每个实例只消费其中的3条消息。
顺序消息:
消费消息的顺序要同发送消息的顺序一致,在RocketMQ中,主要指的是局部顺序,即一类消息为满足顺序性,必须Producer单线程顺序发送,且发送到同一个队列,这样Consumer就可以按照Producer发送的顺序去消费消息。
普通顺序消息:
顺序消息的一种,正常情况下可以保证完全的顺序消息,但是一旦发生通信异常,Broker重启,由于队列总数发生变化,哈希取模后定位的队列会变化,产生短暂的消息顺序不一致。如果业务能容忍在集群异常情况
(如某个Broker宕机或者重启)下,消息短暂的乱序,使用普通顺序方式比较合适。
严格顺序消息:
顺序消息的一种,无论正常异常情况都能保证顺序,但是牺牲了分布式Failover特性,即Broker集群中只要有一台机器不可用,则整个集群都不可用,服务可用性大大降低。如果服务器部署为同步双写模式,此缺陷可
通过备机自动切换为主避免,不过仍然会存在几分钟的服务不可用。(依赖同步双写,主备自动切换,自动切换功能目前还未实现)
目前已知的应用只有数据库binlog同步强依赖严格顺序消息,其他应用绝大部分都可以容忍短暂乱序,推荐使用普通的顺序消息。
Message Queue:
在RocketMQ中,所有消息队列都是持久化,长度无限的数据结构,所谓长度无限是指队列中的每个存储单元都是定长,访问其中的存储单元使用Offset来访问,offset为java long类型,64位,理论上在100年内不会溢出,
所以认为是长度无限,另外队列中只保存最近几天的数据,之前的数据会按照过期时间来删除。也可以认为Message Queue是一个长度无限的数组,offset就是下标。
3. 消息中间件需要解决哪些问题?
1). Publish/Subscribe
2). Message Priority
3). Message Order
4). Message Filter
5). Message Persistence
6). Message Reliablity:异步双写,同步双写(同步双写势必会影响性能,适合对消息可靠性要求极高的场合,例如与Money相关的应用。)
7). Low Latency Messaging:
8). At least Once:是指每个消息必须投递一次,RocketMQ Consumer先pull消息到本地,消费完成后,才向服务器返回ack,如果没有消费一定不会ack消息,所以RocketMQ可以很好的支持此特性。
9). Exactly Only Once:
a. 发送消息阶段,不允许发送重复的消息。
b. 消费消息阶段,不允许消费重复的消息。
只有以上两个条件都满足情况下,才能认为消息是“Exactly Only Once”,而要实现以上两点,在分布式系统环境下,不可避免要产生巨大的开销。所以RocketMQ为了追求高性能,并不保证此特性,
要求在业务上进行去重,也就是说消费消息要做到幂等性。RocketMQ虽然不能严格保证不重复,但是正常情况下很少会出现重复发送、消费情况,只有网络异常,Consumer启停等异常情况下会出现消息重复。
此问题的本质原因是网络调用存在不确定性,即既不成功也不失败的第三种状态,所以才产生了消息重复性问题。
10). Broker的Buffer满了怎么办?
RocketMQ没有内存Buffer概念,RocketMQ的队列都是持久化磁盘,数据定期清除。
对于此问题的解决思路,RocketMQ同其他MQ有非常显著的区别,RocketMQ的内存Buffer抽象成一个无限长度的队列,不管有多少数据进来都能装得下,这个无限是有前提的,Broker会定期删除过期的数据,
例如Broker只保存3天的消息,那么这个Buffer虽然长度无限,但是3天前的数据会被从队尾删除。
CORBA Notification规范中处理方式:
a. RejectNewEvents
b. 按照特定策略丢弃已有消息
11). 回溯消费: RocketMQ支持按照时间回溯消费,时间维度精确到毫秒,可以向前回溯,也可以向后回溯。
12). 消息堆积: 消息中间件的主要功能是异步解耦,还有个重要功能是挡住前端的数据洪峰,保证后端系统的稳定性,这就要求消息中间件具有一定的消息堆积能力
13). 分布式事务
14). 定时消息:
定时消息是指消息发到Broker后,不能立刻被Consumer消费,要到特定的时间点或者等待特定的时间后才能被消费。如果要支持任意的时间精度,在Broker层面,必须要做消息排序,如果再涉及到持久化,那么消息排序
要不可避免的产生巨大性能开销。
RocketMQ支持定时消息,但是不支持任意时间精度,支持特定的level,例如定时5s,10s,1m等。
15). 消息重试:
Consumer消费消息失败后,要提供一种重试机制,令消息再消费一次。Consumer消费消息失败通常可以认为有以下几种情况
a.由于消息本身的原因,例如反序列化失败,消息数据本身无法处理(例如话费充值,当前消息的手机号被注销,无法充值)等。
这种错误通常需要跳过这条消息,再消费其他消息,而这条失败的消息即使立刻重试消费,99%也不成功,所以最好提供一种定时重试机制,即过10s 秒后再重试。
b.由于依赖的下游应用服务不可用,例如db 连接不可用,外系统网络不可达等。
遇到这种错误,即使跳过当前失败的消息,消费其他消息同样也会报错。这种情况建议应用sleep 30s,再消费下一条消息,这样可以减轻Broker 重试消息的压力。
4. RocketMQ 是什么?
1). 是一个队列模型的消息中间件,具有高性能、高可靠、高实时、分布式特点。
2). Producer、Consumer、队列都可以分布式。
3). Producer 向一些队列轮流发送消息,队列集合称为Topic,Consumer 如果做广播消费,则一个consumer
4). 实例消费这个Topic 对应的所有队列,如果做集群消费,则多个Consumer 实例平均消费这个topic 对应的队列集合。
5). 能够保证严格的消息顺序
6). 提供丰富的消息拉取模式
7). 高效的订阅者水平扩展能力
8). 实时的消息订阅机制
9). 亿级消息堆积能力
10). 较少的依赖
5. RocketMQ 网络部署特点:
1). Name Server 是一个几乎无状态节点,可集群部署,节点之间无任何信息同步。
2). Broker 部署相对复杂,Broker 分为Master 与Slave,一个Master 可以对应多个Slave,但是一个Slave 只能对应一个Master,Master 与Slave 的对应关系通过指定相同的BrokerName,不同的BrokerId 来定义,
BrokerId为0 表示Master,非0 表示Slave。Master 也可以部署多个。每个Broker 与Name Server 集群中的所有节点建立长连接,定时注册Topic 信息到所有Name Server。
3). Producer 与Name Server 集群中的其中一个节点(随机选择)建立长连接,定期从Name Server 取Topic 路由信息,并向提供Topic 服务的Master 建立长连接,且定时向Master 发送心跳。Producer 完全无状态,可集群部署。
4). Consumer 与Name Server 集群中的其中一个节点(随机选择)建立长连接,定期从Name Server 取Topic 路由信息,并向提供Topic 服务的Master、Slave 建立长连接,且定时向Master、Slave 发送心跳。Consumer既可以
从Master 订阅消息,也可以从Slave 订阅消息,订阅规则由Broker 配置决定。
6. RocketMQ 逻辑部署结构:
1). Producer Group
用来表示一个发送消息应用,一个Producer Group 下包含多个Producer 实例,可以是多台机器,也可以是一台机器的多个进程,或者一个进程的多个Producer 对象。
一个Producer Group 可以发送多个Topic消息,Producer Group 作用如下:
1. 标识一类Producer
2. 可以通过运维工具查询这个发送消息应用下有多个Producer实例
3. 发送分布式事务消息时,如果Producer中途意外宕机,Broker会主动回调Producer Group内的任意一台机器来确认事务状态。
2). Consumer Group
用来表示一个消费消息应用,一个Consumer Group下包含多个Consumer实例,可以是多台机器,也可以是多个进程,或者是一个进程的多个Consumer对象。一个Consumer Group下的多个Consumer以均摊方式
消费消息,如果设置为广播方式,那么这个Consumer Group下的每个实例都消费全量数据。
7. RocketMQ存储特点:
文件系统
RocketMQ 选择Linux Ext4 文件系统,原因如下:
Ext4 文件系统删除1G 大小的文件通常耗时小于50ms,而Ext3 文件系统耗时约1s 左右,且删除文件时,磁盘IO 压力极大,会导致IO 写入超时。
文件系统层面需要做以下调优措施
文件系统IO 调度算法需要调整为deadline,因为deadline 算法在随机读情况下,可以合并读请求为顺序跳跃方式,从而提高读IO 吞吐量。
8. RocketMQ 关键特性
1). 单机支持1 万以上持久化队列
a. 所有数据单独存储到一个Commit Log,完全顺序写,随机读。
b. 对最终用户展现的队列实际只存储消息在Commit Log 的位置信息,并且串行方式刷盘。
这样做的好处如下:
a. 队列轻量化,单个队列数据量非常少。
b. 对磁盘的访问串行化,避免磁盘竟争,不会因为队列增加导致IOWAIT增高。
每个方案都有缺点,它的缺点如下:
a. 写虽然完全是顺序写,但是读却变成了完全的随机读。
b. 读一条消息,会先读Consume Queue,再读Commit Log,增加了开销。
c. 要保证Commit Log与Consume Queue完全的一致,增加了编程的复杂度。
以上缺点如何克服:
a.随机读,尽可能让读命中PAGECACHE,减少IO读操作,所以内存越大越好。如果系统中堆积的消息过多,读数据要访问磁盘会不会由于随机读导致系统性能急剧下降,答案是否定的。
a). 访问PAGECACHE时,即使只访问1k的消息,系统也会提前预读出更多数据,在下次读时,就可能命中内存。
b). 随机访问Commit Log磁盘数据,系统IO调度算法设置为NOOP方式,会在一定程度上将完全的随机读变成顺序跳跃方式,而顺序跳跃方式读较完全的随机读性能会高5倍以上,可参见以下针对
各种IO方式的性能数据。http://stblog.baidu-tech.com/?p=851
另外4k的消息在完全随机访问情况下,仍然可以达到8K次每秒以上的读性能。
b. 由于Consume Queue存储数据量极少,而且是顺序读,在PAGECACHE预读作用下,Consume Queue的读性能几乎与内存一致,即使堆积情况下。所以可认为Consume Queue完全不会阻碍读性能。
c. Commit Log中存储了所有的元信息,包含消息体,类似于Mysql、Oracle的redolog,所以只要有Commit Log在,Consume Queue即使数据丢失,仍然可以恢复出来。
2). 刷盘策略
RocketMQ 的所有消息都是持久化的,先写入系统PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据,访问时,直接从内存读取。
a.异步刷盘:
b.同步刷盘:
同步刷盘与异步刷盘的唯一区别是异步刷盘写完PAGECACHE 直接返回,而同步刷盘需要等待刷盘完成才返回
3). 消息查询
a.按照Message Id 查询消息:MsgId 总共16 字节,包含消息存储主机地址,消息Commit Log offset。从MsgId 中解析出Broker 的地址和Commit Log 的偏移地址,然后按照存储格式所在位置消息buffer 解析成一个完整的消息。
b.按照Message Key 查询消息:根据查询的key 的hashcode%slotNum得到具体的槽的位置……
4). 服务器消息过滤
5). 长轮询Pull
6). 顺序消息:按顺序放入同一个队列
7). 事务消息:
8). 发送消息负载均衡:发送消息通过轮询队列的方式发送,每个队列接收平均的消息量。通过增加机器,可以水平扩展队列容量。另外也可以自定义方式选择发往哪个队列。
9). 订阅消息负载均衡:如果有5 个队列,2 个consumer,那么第一个Consumer 消费3 个队列,第二consumer 消费2 个队列。这样即可达到平均消费的目的,可以水平扩展Consumer 来提高消费能力。
但是Consumer 数量要小于等于队列数量,如果Consumer 超过队列数量,那么多余的Consumer 将不能消费消息。
10). 单队列并行消费:单队列并行消费采用滑动窗口方式并行消费,可以有多个线程并行消费,但是每次提交的Offset 都是最小Offset
11). 发送定时消息
12). 消息消费失败,定时重试
13). HA,同步双写/异步复制:异步复制的实现思路非常简单,Slave 启动一个线程,不断从Master 拉取Commit Log 中的数据,然后在异步build 出Consume Queue 数据结构。整个实现过程基本同Mysql 主从同步类似。
14). 单个JVM 进程也能利用机器超大内存
15). 消息堆积问题解决办法:
在有Slave情况下,Master一旦发现Consumer访问堆积在磁盘的数据时,会向Consumer下达一个重定向指令,令Consumer从Slave拉取数据,这样正常的发消息与正常消费的Consumer都不会因为消息堆积受影响,
因为系统将堆积场景与非堆积场景分割在了两个不同的节点处理。这里会产生另一个问题,Slave会不会写性能下降,答案是否定的。因为Slave的消息写入只追求吞吐量,不追求实时性,只要整体的吞吐量高就可以,
而Slave每次都是从Master拉取一批数据,如1M,这种批量顺序写入方式即使堆积情况,整体吞吐量影响相对较小,只是写入RT会变长。
9. RocketMQ消息过滤
1). 简单消息过滤:简单消息过滤通过指定多个Tag 来过滤消息,过滤动作在服务器进行。
2). 高级消息过滤:
a.Broker 所在的机器会启动多个FilterServer 过滤进程
b.Consumer 启动后,会向FilterServer 上传一个过滤的Java 类
c.Consumer 从FilterServer 拉消息,FilterServer 将请求转发给Broker,FilterServer 从Broker 收到消息后,按照Consumer 上传的Java 过滤程序做过滤,过滤完成后返回给Consumer。
总结:
a. 使用CPU 资源来换取网卡流量资源
b. FilterServer 与Broker 部署在同一台机器,数据通过本地回环通信,不走网卡
c. 一台Broker 部署多个FilterServer,充分利用CPU 资源,因为单个Jvm 难以全面利用高配的物理机Cpu 资源
d. 因为过滤代码使用Java 语言来编写,应用几乎可以做任意形式的服务器端消息过滤,例如通过Message Header进行过滤,甚至可以按照Message Body 进行过滤。
e. 使用Java 语言进行作为过滤表达式是一个双刃剑,方便了应用的过滤操作,但是带来了服务器端的安全风险。需要应用来保证过滤代码安全,例如在过滤程序里尽可能不做申请大内存,创建线程等操作。避免Broker 服务器
发生资源泄漏。
10. RocketMQ 通信组件:
RocketMQ 通信组件使用了Netty-4.0.9.Final,在之上做了简单的协议封装。
1). 网络协议
2). 心跳处理:通信组件本身不处理心跳,由上层进行心跳处理。
3). 连接复用:同一个网络连接,客户端多个线程可以同时发送请求,应答响应通过header中的opaque字段来标识。
4). 超时连接:如果某个连接超过特定时间没有活动(无读写事件),则自动关闭此连接,并通知上层业务,清除连接对应的注册信息。
11. RocketMQ服务发现(Name Server)
Name Server是专为RocketMQ设计的轻量级名称服务,代码小于1000行,具有简单、可集群横向扩展、无状态等特点。将要支持的主备自动切换功能会强依赖Name Server。
参见:http://alibaba.github.io/RocketMQ-docs/document/design/RocketMQ_design.pdf
文章推荐:https://www.jianshu.com/p/6868ddceaa5b
http://dbaplus.cn/news-21-1123-1.html
https://www.jianshu.com/p/453c6e7ff81c
rocketmq-console:https://www.cnblogs.com/quchunhui/p/7284752.html
rocketMQ常用命令(文章中'-' 有问题,需要替换成自己的 '-' ,否则命令不可运行):http://blog.csdn.net/zhu_tianwei/article/details/40951301