RocketMQ-生产者发送消息零丢失方案

无天有壁纸 2024-05-05 22:38:20
解决消息丢失的第一个问题:生产者推送消息丢失RocketMQ有一个非常强悍有力得到功能,就是事务消息,凭借这个事务级的消息机制,就可以让我们保证生产者推送出去的消息一定会成功写入MQ里,绝不会半路就搞丢了。 首先以一个订单系统为例,假设他收到了一个订单支付成功的通知之后,他必然是需要在自己的订单数据库里做一些增删改查操作的,比如更新订单状态之类的。 可能你会觉得,订单系统不就是先在自己的数据库做一些操作,然后直接发个消息到MQ去,让其他订阅这个Topic的系统去从MQ获取消息做对应的处理就可以了吗? 其实还真不是这么简单,在基于RocketMQ的事务消息机制中,我们首先要让订单系统去发送一条half消息到MQ,这个half消息本质就是一个订单支付成功的消息,只不过你可以理解为他这个消息的状态是half状态,这个时候消费者系统是看不见这个half消息的。然后我们去等待接收这个half消息写入成功的响应通知,可能现在你会觉得你没事儿先发个half消息给MQ做什么? 其实可以想一下,加入你二话不说让订单系统直接做了本地数据库的操作,然后再发送消息给MQ,结果爆出一堆异常,发现MQ挂了。这个时候,必然导致你没法通过消息通知下游消费者系统去做一些列的逻辑。 所以这里我们首先第一件事,不是让订单系统做一些数据库操作,而是先发一个half消息给MQ以及收到他的成功的响应,初步先跟MQ做了联系和沟通。意思就是确认下MQ还活着。 如果half消息写入失败了,可能你发现报错了,可能MQ就挂了,或者网络出现故障,导致half消息没有发送成功。 这个时候你的订单系统应该执行一系列的回滚操作,比如对订单状态做一个更新,把状态改成"交易关闭"等等。 如果half消息写入成功,这个时候订单系统应该在自己的数据库里执行一些增删改查的操作。 如果订单系统更新自己的数据库失败了,比如数据库的网络异常,或者数据库系统挂了,这个时候其实很简单,就是直接让订单系统发送一个rollback请求给MQ就可以了,意思就是说可以把之前发送给你的half消息给删除了,因为我自己这里都出问题了,已经没有办法继续后续的流程了。 如果订单系统成功的完成了本地的事务操作,此时就可以发送一个commit请求给MQ,要求让MQ对之前提交的half消息进行commit操作,RocketMQ对half消息进行commit之后,下游消费者系统就可以看到这个消息了。 如果发送half消息成功了,但是没有收到响应呢? 如果我们把half消息发送给MQ了,MQ给保存下来了,但是MQ返回给我们的响应我们没有收到,此时会发生什么事情? 这个时候我们没有收到响应,可能是网络超时报错或者其他异常错误,这个时候订单系统会误以为发送half消息到MQ失败了,订单系统就直接执行回滚逻辑,把状态标记成"已关闭"。 但是这个时候MQ已经存储下来一条half消息了,那对这个消息要怎么处理? 其实RocketMQ这里有一个补偿流程, 他会去扫描自己处于half状态的消息,如果我们一直没有对这个消息执行commit/rollback操作,超过一定时间之后,他会回调你得到订单系统的一个接口,来确认到底是要commit这个消息还是rollback这个消息。 我们系统的这个接口就需要去判断这个消息是要commit还是rollback。 如果rollback或者commit发送失败了呢? 再假设一种场景,如果订单系统是收到了half消息写入成功的响应,同时尝试对自己数据库进行了更新,然后根据失败或者成功去执行了rollback或者commit请求,发送给MQ了,结果因为网络故障,导致rollback或者commit请求发送失败了呢? 其实这个也很简单,因为MQ里的消息一直是half状态,所以他过了一定时间会发现这个half消息有问题,会触发回调接口补偿的。 事务消息机制的底层实现原理half消息是如何对消费者不可见的? 对于MQ中的half消息,在没有commit操作之前,下游消费者是看不到他的,没办法去消费这条消息,那这个half消息是如何做到不给下游消费者系统看到的呢? 先举个例子,订单系统发送了一个half状态的订单支付消息到"OrderPaySuccessTopic"里去,这是一个Topic,然后下游红包系统也是订阅了这个"OrderPaySuccessTopic",从里面获取消息的。其实你写入一个Topic,最终是定位到这个Topic的某个MessageQueue,然后定位到一台Broker机器上去,然后写入的是Broker上的CommitLog文件,同时将消费索引写入到MessageQueue对应的ConsumeQueue文件。 如果你写入一条half消息到OrderPaySuccessTopic里去,会定位到这个Topic的一个MessageQueue,然后定位到一个Broker机器上去,接着按理说消息会写入CommitLog,同时消息的offset会写入MessageQueue对应的ConsumeQueue,这个ConsumeQueue是属于OrderPaySuccessTopic的,然后下游红包系统按理说会从这个ConsumeQueue里获取到你写入的这个half消息。 但实际上红包系统却没有办法看到这条消息,其本质原因就是RocketMQ一旦发现你发送的是一个half消息,他不会把整个half消息的offset写入OrderPaySuccessTopic的ConsumeQueue里去,他会把这条half消息写入到自己内部的"RMQ_SYS_TRANS_HALF_TOPIC"这个Topic对应的一个ConsumeQueue里去。所以下游的红包系统没有办法看到这个half消息。 在什么情况下订单系统会收到half消息成功的响应? 结合上面的内容,可以了解到,必须是要half消息进入到RocketMQ内部的RMQ_SYS_TRANS_HALF_TOPIC的ConsumeQueue文件了,此时就会认为half消息写入成功了,然后就会返回响应给订单系统。 这个时候,一旦你的订单系统收到这个half消息写入成功的响应,必然就知道这个half消息已经在RocketMQ的内部了。 假如因为各种问题,没有执行rollback或者commit会怎么样? 其实这个时候他会在后台有定时任务,定时任务会去扫描RMQ_SYS_TRANS_HALF_TOPIC中的half消息,如果你超过一定时间还是half消息,rocketMQ会回调订单系统的接口,让你判断这个half消息是要rollback还是commit. 如果执行rollback操作的话,如何标记消息回滚? 假设我们的订单系统执行了rollback请求,那么此时就需要对消息进行回滚,但是RocketMQ会把这条half消息从磁盘文件中删除吗? 显然是不是的,因为RocketMQ都是顺序的把消息写入磁盘文件的,所以在这里如果你执行rollback,他的本质就是用一个OP操作来标记half消息的状态。 RocketMQ内部有一个OP_TOPIC,此时可以写一条rollback op记录到这个Topic,标记某个half消息时rollback了。 另外,假如你一直没有执行commit/rollback,RocketMQ会回调订单系统的接口去判断half消息的状态,但是他最多就会回调15次,如果15次之后你都没有办法告诉他half消息的状态,他就自动把这条half消息标记成rollback 如果执行commit操作,如何让消息对下游消费者可见? 在执行commit操作之后,RocketMQ就会在OP_TOPIC里写入一条记录,标记half消息已经是commit状态了,接着需要把放在RMQ_SYS_TRANS_HALF_TOPIC中的half消息给写入到OrderPaySuccessTopic的ConsumeQueue里去,这样下游消费系统就可以消费这条消息了。
0 阅读:0

无天有壁纸

简介:感谢大家的关注