RocketMQ高级之事务消息以及重复消息解决方案

RocketMQ高级之事务消息&重复消息解决方案

RocketMQ事务消息原理

Half(Prepare) Message

        指的是暂不能投递给消费者的消息,发送方已经将消息成功发送到了 MQ 服务端,但是服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即半消息。

Message Status Check

        由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,MQ 服务端通过扫描发现某条消息长期处于“半消息”时,需要主动向消息生产者询问该消息的最终状态(Commit 或是 Rollback),该过程即消息回查。

1.发送方向MQ服务端发送消息。
2.MQ Server将消息持久化成功之后,向发送方ACK确认消息已经发送成功,此时消息为半消息。3.发送方开始执行本地事务逻辑。
4.发送方根据本地事务执行结果向MQ Server提交二次确认(Commit或是Rollback ) ,MQ Server收到Commit状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server收到Rollback状态则删除半消息,订阅方将不会接受该消息。
5.在断网或者是应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达 MQ Server,经过固定时间后MQ Server将对该消息发起消息回查。
6.发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
7.发送方根据检查得到的本地事务的最终状态再次提交二次确认,MQ Server仍按照步骤4对半消息进行操作。

生产者业务逻辑关键,发送消息、确认消息

// 设置事务监听器 producer.setTransactionListener(new TransactionListenerImpl());

消费端逻辑不变。

消费者消费方式

在RocketMQ中,消费者有两种模式,一种是push模式,另一种是pull模式。

push模式:客户端与服务端建立连接后,当服务端有消息时,将消息推送到客户端。

pull模式:客户端不断的轮询请求服务端,来获取新的消息。

但在具体实现时,Push和Pull模式都是采用消费端主动拉取的方式,即consumer轮询从broker拉取消息。

区别:

Push方式里,consumer把轮询过程封装了,并注册MessageListener监听器,取到消息后,唤醒 MessageListener的consumeMessage()来消费,对用户而言,感觉消息是被推送过来的。

Pull方式里,取消息的过程需要用户自己写,首先通过打算消费的Topic拿到MessageQueue的集合,遍历 MessageQueue集合,然后针对每个MessageQueue批量取消息,一次取完后,记录该队列下一次要取的开 始offset,直到取完了,再换另一个MessageQueue。

疑问:既然是采用pull方式实现,RocketMQ如何保证消息的实时性呢?

长轮询

长轮询即是在请求的过程中,若是服务器端数据并没有更新,那么则将这个连接挂起,直到服务器推送新的数据,再返回,然后进入循环周期。 客户端像传统轮询一样从服务端请求数据,服务端会阻塞请求不会立刻返回,直到有数据或超时才返回给客 户端,然后关闭连接,客户端处理完响应信息后再向服务器发送新的请求。

DefaultMQPushConsumer实现了自动保存offset值以及实现多个consumer的负载均衡。

通过groupname将多个consumer组合在一起,那么就会存在一个问题,消息发送到这个组后,消息怎么分配呢? 这个时候,就需要指定消息模式,分别有集群和广播模式。

集群模式(默认)同一个 ConsumerGroup(GroupName相同) 里的每 个 Consumer 只消费所订阅消息的一部分内容, 同 一个 ConsumerGroup 里所有的 Consumer消费的内容合起来才是所订阅 Topic 内容的整体, 从而达到 负载均衡的目的 。

广播模式 同一个 ConsumerGroup里的每个 Consumer都 能消费到所订阅 Topic 的全部消息,也就是一个消息会 被多次分发,被多个 Consumer消费。

重复消息的解决方案

造成消息重复原因是:网络不可达。只要通过网络交换数据,就无法避免这个问题。所以解决这个问题的办 法就是绕过这个问题。那么问题就变成了:如果消费端收到两条一样的消息,应该怎样处理?

//设置组名 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("IM"); 

// 集群模式 consumer.setMessageModel(MessageModel.CLUSTERING);

// 广播模式 consumer.setMessageModel(MessageModel.BROADCASTING); 

1. 消费端处理消息的业务逻辑保持幂等性

2. 保证每条消息都有唯一编号且保证消息处理成功与去重表的日志同时出现

第1条很好理解,只要保持幂等性,不管来多少条重复消息,最后处理的结果都一样。第2条原理就是利用一张日志 表来记录已经处理成功的消息的ID,如果新到的消息ID已经在日志表中,那么就不再处理这条消息。

第1条解决方案,很明显应该在消费端实现,不属于消息系统要实现的功能。

第2条可以消息系统实现,也可以业务 端实现。正常情况下出现重复消息的概率其实很小,如果由消息系统来实现的话,肯定会对消息系统的吞吐量和高 可用有影响,所以最好还是由业务端自己处理消息重复的问题,这也是RocketMQ不解决消息重复的问题的原因。

RocketMQ不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值