7.RocketMQ关键特性
(1)单机支持1万以上持久化队列
1.所有数据单独存储到一个Commit Log,完全顺序写,随机读。
2.对最终用户展现的队列实际只存储消息在Commit Log的位置信息,并且串行方式刷盘。
这样好处:
a、队列轻量化,单个队列的数据量非常少。
b、对磁盘的访问串行化,避免磁盘竞争,不会因为队列增加导致IO wait 增高。
每个方案都有缺点,它的缺点如下:
(a)写虽然完全是顺序写,但是读却变成了完全的随机读、
(b)读一条消息,会先读Consume Queue,再读Commit Log,增加了开销。
(c)要保证Commit Log 与Consume Queue完全的一致,增加了编程的复杂度。
(2)刷盘策略
RocketMQ所有的消息都是持久化的,先写入系统PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据,访问时,直接从内存读取。
(a)异步刷盘
(b)同步刷盘
同步刷盘与异步刷盘的唯一区别是异步刷盘写完PAGECACHE直接返回,而同步刷盘需要等待刷盘完成后才能返回。
同步刷盘流程如下:
(1)写入PAGECACHE后,线程等待,通知刷盘线程刷盘。
(2)刷盘线程刷盘完成后,唤醒前端等待线程,可能是一批线程。
(3)前端等待线程向用户返回成功。
3.消息查询
(a)按照Message Id查询消息
MsgId总共16字节,包含消息存储主机地址,消息Commit Log offset,从MsgId中解析出Broker的地址和Commit Log Offset的偏移地址,然后按照存储格式所在位置消息buffer解析成一个完整的消息。
(b)按照Message key查询消息
1.根据查询的key的hashcode%slotNum得到具体地槽的位置(slot是最大槽数,图中为5000000)
2.根据slotValue(slot位置对应的值)查找到索引项列表的最后一项(倒序排序,slotVlaue总是指向最新一个索引项)
3.遍历索引项列表返回查询时间范围内的结果集(默认一次最大返回32条记录)
4.Hash冲突:寻找key的slot位置时相当于执行了两次散列函数,一次key的hash,一次key的hash值取模,因此存在两次冲突的情况:a、key的hash值不同但模数相同,此时查询的时候会再比较一次key的hash值(每个索引项保存了key的hash值),过滤掉hash值不相等的项。第二种,hash值相同key不同,出于性能的考虑冲突的监测放到客户端处理(key原始值是存储在消息文件中,避免对数据文件的解析),客户端比较一次消息体的可以是否相同。
5.存储:为了节省空间索引项中存储的时间是时间差值(存储时间-开始时间,开始时间存储在索引文件头中,整个索引文件是定长的,结构也是固定的)
4.服务器消息过滤
RocketMQ的消息过滤方式有别与其他消息中间件,是在订阅时,再做过滤,先看下Consumer Queune结构
(1)在Broker端进行Message Tag比对,先遍历Consume Queue,如果存储的Message Tag 与订阅的Message Tag不符合,则跳过,继续比对下一个,符合则传输给Consumer,注意,Message Tag是字符串形式,ConsuemerQueue中存储的是其对应的hashcode,比对时也是hashcode。
(2)Consumer收到过滤后的消息后,同样也要执行在Broker端的操作,但是比对的是真实的Message Tag字符串,而不是hashcode。
为什么过滤要这样做?
a、Message Tag存储Hashcode,是为了在Consume Queue定长方式存储,节约空间。
b、过滤过程中不会访问Commit Log数据,可以保证堆积情况下也能高效过滤。
c、即使存在hash冲突,也可以在Consumer端进行修正,保证万无一失。、
5.长轮询Pull
RocketMQ的consumer都是从Broker拉消息来消费,但是为了做到实时收消息,RocketMQ使用长轮询方式,可以保证消息实时性同Push方式一致,这种长轮询方式类似于Web QQ收发消息机制,
6.顺序消息原理
顺序消息缺陷:
发送顺序消息无法利用集群FailOver特性
消费顺序的并行度依赖于队列数量
队列热点问题,个别队列由于哈希不均导致消息过多,消费速度跟不上,产生消息堆积问题
遇到消息失败的消息,无法跳过,当前对列消费暂停
7.事务消息
8.发送消息负载均衡
5个队列可以部署在一台机器上,也可以分别部署在5台不同的机器上,发送消息通过轮询队列的方式发送,每个队列接收平均的消息量,通过增加机器,可以水平扩展队列容量。也可以自定义方式选择发往哪个队列。
9.订阅消息负载均衡
如图,若有5个队列,2个consumer,那么第一个Consumer消费3个队列,第二consumer消费2个队列。这样可以达到平均消费的目的,可以水平扩展Consumer来提高消费能力,但是Consumer数量要小于等于队列数量,如果Consumer超过队列数量,那么多余的Consumer将不能消费消息
消息堆积问题