1.相关模块
1.1 Producer
将消息发送到Broker
1.2 Producer组
发送同一种消息的Produder就可以构成一个Producer集群。同一种消息指的是消息的Topic和Tag相同
1.3 Consumer
Broker将消息投递到Consumer
1.4 Consumer组
同一个GroupName,并且订阅同一个Topic下的同中Tag的Consumer,就可以构成一个Consumer组。
1.5 NameServer(可集群)
维护集群中所有的节点信息,类似于dubbo中的zk。和Broker每个节点建立长连接,利用心跳机制获取Broker集群中的可用节点,并缺获取Broker中的Topic信息。Producer会和NameServer中随机一个节点建立连接,获取Topic信息。Producer发送消息时,通过NameServer提供的信息,找到Topic对应的的Broker,建立长连接,这个Broker只能是Master节点。Consumer会和NameServer中的随机一个节点建立连接,获取Topic信息。Consumer会根据NameServer提供的信息,找到订阅的Topic对应的Broker,建立长连接。这个Broker可以是Master节点,也可以是Slaver节点。
1.6 Broker(可集群)
Broker接受来自Producer的消息并存储,投递消息给消费者。内部有Topic的概念,一个Topic中可对应多个Queue。Queue对应Consumer端来说是透明的。
Broker内部是可以集群的,分为Master和Slaver,类似与Mysql的Master和Slaver。
1.7 Message
消息实体,在MQ中进行传输的就是Message,Message有Topic和Tag的概念。通过Topic,Producer就可以知道消息要发送到哪个Broker。Consumer在创建时需要订阅指定的Topic和Tag,通过Topic和对应的Broker建立连接。Broker投递时,再根据Tag推送消息到Consumer中。
1.8 关系图
2.部署方式
2.1 单master
只有一个节点,该节点挂掉,整个服务将不可用。部署比较简单,但有单点问题。
2.2 多master
多个master组成集群,单个master挂掉对集群没有影响。需要开启同步刷盘模式,来保证某个节点挂掉时消息不丢失,Topic也需要均匀分布在每Master上,保证某个节点挂了,还能提供服务。
2.3 多master,多slaver异步复制
多个master对应多个slaver,master提供读写功能,slaver提供读功能,master和slaver之间通过异步复制保证数据的同步,类似mysql的主从模式。不过可能会存在消息丢失情况,master挂掉时,对应的数据还没有同步到slaver上。
2.4 多master,多slaver同步双写
多个master对应多个slaver,和异步复制的区别是。数据同步的方式是同步的,保证master和slaver的数据一致,这样就会导致发送消息的响应时间较异步复制长一点,不过能够保证消息不丢失。
3.消息类型
3.1 普通消息
消息只管往broker里面丢,不保证consumer端消费的顺序性
3.2 有序消息
保证consumer消费时的顺序性,特定的一些场景下必须使用有序消息来实现。比如订单的创建,付款和完成。有序消息又分为全局有序和局部有序。
全局有序:一个Topic中只创建一个Queue,所有的消息都丢到这个Queue中,就能保证消费端消费的有序,不过吞吐量很低。
局部有序: 一个Topic中可以创建多个Queue,思想就是保证同一个对象的不同事件都得落在同一个Queue上。建议通过hash取模的方式来做,即可以保证一个对象的不同时间在一个queue,也可以保证每个queue的充分利用。
3.3 延时消息
消息发送到broker中,不会立即投递到consumer,而是会等待到指定时间再进行投递。RcoketMQ的延时等级为:1s,5s,10s,30s,1m,2m,3m,4m,5m,6m,7m,8m,9m,10m,20m,30m,1h,2h。level=0,表示不延时。level=1,表示 1 级延时,对应延时 1s。level=2 表示 2 级延时,对应5s,以此类推。
4.消息发送方式
4.1 同步发送
producer发送消息到broker后,需要等待broker响应,再发送下一条消息,类似于TCP中的停止等待ARQ。可以保证消息的实时可靠性,比如通知邮件,营销短信。
4.2 异步发送
producer发送消息到broker后,不需要等待broker响应,可以继续发送下一条消息,不过broker还是会给producer响应,类似于TCP的连续等待ARQ。比如需要花一些时间处理完再给响应的,也是可以保证消息的可靠性,不过不是实时的。
4.3 单向发送
producer只管发送消息给broker,不管收不收到响应,不能保证消息的可靠性。特点就是快,使用于耗时短,可靠性要求不高的场景
5.消息消费模式
当一堆consumer实例使用同一个group name,并且订阅的Topic和Tag一样时,才构成一个Consumer集群。
5.1 集群消费
consumer 的消费进度是存储在 broker 上,consumer 自身不存储消费进度。每条消息只会被 consumer 集群内的任意一个 consumer 实例消费一次。比如有3个consumer,消息如果投递到其中一个被正常消费,那么这个消息就算被消费了,其他两个consumer不会消费的这个消息。集群消费模式提供消息重投机制,广播消费则没有,不过消费重投不能保证重投的消息还是投递到之前消费失败的那一台机器上。
5.2 广播消费
与集群消费不同,consumer 的消费进度是存储在各个 consumer 实例上。消息会被推送到相关的所有consumer上,比如又3个consumer,消息会投递到这3个consumer上,不管消费成不成功。而且不会进行消费失败重投,所以在 consumer 端消费逻辑处理时,需要额外关注消费失败的情况。
5.3 集群模拟广播消费
如果业务上确实需要使用广播消费,但又想利用消息重投机制。可以通过创建多个 consumer 实例,每个 consumer 实例属于不同的 consumer group,但是它们都订阅同一个 topic。比如:有3个consumer,这3个consumer属于不同的group,但它们都订阅topic A。当broker要投递消息时,这3个consumer都能收到消息。
每个consumer的消费逻辑可以一样,也可以不一样。每个consumer group还可以根据需要增加内部的consumer实例。
6.刷盘方式
RocketMQ的消息是存储到磁盘上的,这样既能保证断电后恢复,又可以让存储的消息量超出内存的限制。
RocketMQ为了提高性能,会尽可能地保证磁盘的顺序写。
6.1 异步刷盘
在返回写成功状态时,消息可能只是被写入了内存的PAGECACHE,写操作的返回快,吞吐量大。当内存里的消息量积累到一定程度时,统一触发写磁盘操作,快速写入。缺点是可能导致数据丢失,在数据还没有被写入磁盘,机器宕机,就会丢失数据。
6.2 同步刷盘
在返回写成功状态时,消息已经被写入磁盘。具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息写成功的状态。缺点是磁盘io次数多,效率较低,响应时间较长。
6.3 生产应用
在生产中,一般使用异步刷盘方式,来增大系统吞吐量。通过broker主从数据同步双写来保证数据不被丢失。
7.小细节
7.1 消息过滤
利用tag机制,可以实现消息过滤。我们在发送消息时,可以给message打上tag,在consumer订阅时,指定对应的topic中的特定tag,就可以实现消费端的消息过滤。比如:创建一个Trade的Topic,producer在发送Trade的消息时,可以打上order,pay,logistic的tag。关于订单的consumer指定order的tag,关于支付的consumer指定pay的tag,关于物流的consumer指定logistic的tag。
7.2 订阅关系一致性
订阅关系一致性指的是同一个Consumer group中,每一个consumer订阅的topic和tag必须相同,才能称之为一个Consumer group。要保证订阅关系一致性,就必须同时保证两点。1.订阅的topic一致 2.订阅的tag也必须一致
比如: consumer1订阅了 topic A的tag a || tag b,consumer2也订阅了topic A的tag a || tag b。这两个consumer就可以称之为一个consumer group
7.3 消息重试
消息重试只针对集群消费模式,那么什么是消息重试呢?简单来说,就是当消费者消费消息失败后,broker 会重新投递该消息,直到消费成功。消费失败的情况有3种。
1.返回 ConsumeConcurrentlyStatus.RECONSUME_LATER
2.返回null
3.抛出异常。需要注意的是,如果异常被在消费时发生的异常如果被捕获,是不会重试的。
此外,消息也不会无限重试,因为可能存在一些脏数据或者bug,无论重试多少次都失败。所以,RocketMq提供了一个重试机制:
第几次重试 | 每次重试间隔时间 |
1 | 10 秒 |
2 | 30 秒 |
3 | 1 分钟 |
4 | 2 分钟 |
5 | 3 分钟 |
6 | 4 分钟 |
7 | 5 分钟 |
8 | 6 分钟 |
9 | 7 分钟 |
10 | 8 分钟 |
11 | 9 分钟 |
12 | 10 分钟 |
13 | 20 分钟 |
14 | 30 分钟 |
15 | 1 小时 |
16 | 2 小时 |
如果重投次数超过了16次,那么消息就不会再投递给消费者,而是将消息放到相对应的死信队列中。这时候我们就需要对死信队列的消息做一些人工补偿处理,因为这些消息可能本身就有问题,也有可能和消费逻辑调用的服务有关等,所以需要人工判断之后再进行处理。
7.4 消费幂等
消费幂等,指的就是一个消息无论被消费多少次,结果都是一样的。比如不加乐观锁更新一个商品金额到100块,无论更新多少次,结果都是100块。
导致消息重复消费的情况有两种.
1.producer传递消息到broker中时,broker因为网络波动或者producer宕机,导致producer没有收到broker的响应,producer会重新发送一条消息。
2.broker投递消息到consumer,consumer消费消息成功,但是ack因为网络原因没有回传到broker,导致broker重新投递消息到consumer。
对于一些允许消息重复的场景,可以不必关心消费幂等。但是对于那些不允许消息重复的业务场景来说,可通过业务上的唯一标识来作为幂等处理的依据或者在代码层面来做消费的一个幂等