rocketmq
rocketmq天然支持高可用,它可以支持多主多从的部署架构,这也是和kafka最大的区别之一

原因是RocketMQ中并没有master选举功能,所以通过配置多个master节点来保证rocketMQ的高可用。和所有的集群角色定位一样,master节点负责接受事务请求、slave节点只负责接收读请求,并且接收master同步过来的数据和slave保持一直。当master挂了以后,如果当前rocketmq是一主多从,就意味着无法接受发送端的消息,但是消费者仍然能够继续消费

所以配置多个主节点后,可以保证当其中一个master节点挂了,另外一个master节点仍然能够对外提供消息发送服务。当存在多个主节点时,一条消息只会发送到其中一个主节点,rocketmq对于多个master节点的消息发送,会做负载均衡,使得消息可以平衡的发送到多个master节点上。

一个消费者可以同时消费多个master节点上的消息,在下面这个架构图中,两个master节点恰好可以平均分发到两个消费者上,如果此时只有一个消费者,那么这个消费者会消费两个master节点的数据。

由于每个master可以配置多个slave,所以如果其中一个master挂了,消息仍然可以被消费者从slave节点消费到。可以完美的实现rocketmq消息的高可用

rockmq namesrvaddr 多地址 rocketmq多主多从_消息发送


接下来,站在topic的角度来看看消息是如何分发和处理的,假设有两个master节点的集群,创建了一个TestTopic,并且对这个topic创建了两个队列,也就是分区。

消费者定义了两个分组,分组的概念也是和kafka一样,通过分组可以实现消息的广播。

rockmq namesrvaddr 多地址 rocketmq多主多从_kafka_02


集群支持

RocketMQ天生对集群的支持非常友好

1)单Master

优点:除了配置简单没什么优点

缺点:不可靠,该机器重启或宕机,将导致整个服务不可用

2)多Master

优点:配置简单,性能最高

缺点:可能会有少量消息丢失(配置相关),单台机器重启或宕机期间,该机器下未被消费的消息在机

器恢复前不可订阅,影响消息实时性

3)多Master多Slave,每个Master配一个Slave,有多对Master-Slave,集群采用异步复制方式,主备

有短暂消息延迟,毫秒级

优点:性能同多Master几乎一样,实时性高,主备间切换对应用透明,不需人工干预

缺点:Master宕机或磁盘损坏时会有少量消息丢失

4)多Master多Slave,每个Master配一个Slave,有多对Master-Slave,集群采用同步双写方式,主备

都写成功,向应用返回成功

优点:
服务可用性与数据可用性非常高

缺点:
性能比异步集群略低,当前版本主宕备不能自动切换为主
需要注意的是,在RocketMQ里面,1台机器只能要么是Master,要么是Slave。这个在初始的机器配置里面,就定死了。不会像kafka那样存在master动态选举的功能。其中Master的broker id = 0,Slave的broker id > 0。
有点类似于mysql的主从概念,master挂了以后,slave仍然可以提供读服务,但是由于有多主的存在,当一个master挂了以后,可以写到其他的master上

消息的顺序消费
首先,需要保证顺序的消息要发送到同一个messagequeue中;其次,一个messagequeue只能被一个消费者消费,这点是由消息队列的分配机制来保证的;最后,一个消费者内部对一个mq的消费要保证是有序的。
我们要做到生产者 - messagequeue - 消费者之间是一对一对一的关系

自定义消息发送规则
通过自定义发送策略来实现消息只发送到同一个队列
因为一个Topic 会有多个Message Queue ,如果使用Producer 的默认配置,这个Producer 会轮流向各个Message Queue 发送消息。Consumer 在消费消息的时候,会根据负载均衡策略,消费被分配到的Message Queue
如果不经过特定的设置,某条消息被发往哪个Message Queue ,被哪个Consumer 消费是未知的

如果业务需要我们把消息发送到指定的Message Queue 里,比如把同一类型的消息都发往相同的Message Queue。那是不是可以实现顺序消息的功能呢?
和kafka一样,rocketMQ也提供了消息路由的功能,我们可以自定义消息分发策略,可以实现MessageQueueSelector,来实现自己的消息分发策略

如何保证消息消费顺序呢?
通过分区规则可以实现同类消息在rocketmq上的顺序存储。但是对于消费端来说,如何保证消费的顺序?

RocketMQ中提供了MessageListenerOrderly 一个类来实现顺序消费

consumer.subscribe("store_topic_test","*");
consumer.registerMessageListener((MessageListenerOrderly) (list, 
	consumeOrderlyContext) -> { 
	list.stream().forEach(messageExt -> System.out.println(new String(messageExt.getBody()))); 
	return ConsumeOrderlyStatus.SUCCESS; 
	});

顺序消费会带来一些问题,

  1. 遇到消息失败的消息,无法跳过,当前队列消费暂停
  2. 降低了消息处理的性能

消费端的负载均衡
和kafka一样,消费端也会针对Message Queue做负载均衡,使得每个消费者能够合理的消费多个分区的消息

消费端会通过RebalanceService线程,10秒钟做一次基于topic下的所有队列负载

  • 消费端遍历自己的所有topic,依次调rebalanceByTopic
  • 根据topic获取此topic下的所有queue
  • 选择一台broker获取基于group的所有消费端(有心跳向所有broker注册客户端信息)
  • 选择队列分配策略实例AllocateMessageQueueStrategy执行分配算法

什么时候触发负载均衡

  • 消费者启动之后
  • 消费者数量发生变更
  • 每10秒会触发检查一次rebalance

分配算法
RocketMQ提供了6中分区的分配算法

  • (AllocateMessageQueueAveragely)平均分配算法(默认)
  • (AllocateMessageQueueAveragelyByCircle)环状分配消息队列
  • (AllocateMessageQueueByConfig)按照配置来分配队列: 根据用户指定的配置来进行负载
  • (AllocateMessageQueueByMachineRoom)按照指定机房来配置队列
  • (AllocateMachineRoomNearby)按照就近机房来配置队列:
  • (AllocateMessageQueueConsistentHash)一致性hash,根据消费者的cid进行

消息的的可靠性原则
在实际使用RocketMQ的时候我们并不能保证每次发送的消息都刚好能被消费者一次性正常消费成功,可能会存在需要多次消费才能成功或者一直消费失败的情况,那作为发送者该做如何处理呢?

  • 消息消费端的确认机制
    RocketMQ提供了ack机制,以保证消息能够被正常消费。发送者为了保证消息肯定消费成功,只有使用方明确表示消费成功,RocketMQ才会认为消息消费成功。中途断电,抛出异常等都不会认为成功
  • 消息的衰减重试
    为了保证消息肯定至少被消费一次,RocketMQ会把这批消息重新发回到broker,在延迟的某个时间点(默认是10秒,业务可设置)后,再次投递到这个ConsumerGroup。而如果一直这样重复消费都持续失败到一定次数(默认16次),就会投递到DLQ死信队列。应用可以监控死信队列来做人工干预可以修改broker-a.conf文件
messageDelayLevel = 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
  • 重试消息的处理机制
    一般情况下我们在实际生产中是不需要重试16次,这样既浪费时间又浪费性能,理论上当尝试重复次数达到我们想要的结果时如果还是消费失败,那么我们需要将对应的消息进行记录,并且结束重复尝试