用户注册后,发消息到MQ,然后会员服务监听消息异步处理。有时发现,虽然用户服务先落库再发MQ,但会员服务收到消息后去查询DB,却发现DB还没有新用户信息。why?
1.1 可能原因用户注册的代码端把异常都吞了,没抛出来,而用户注册又报错了,但还是继续执行并发了MQ消息。
可能数据写到了主库,然后查询的从库。但因主从延迟,导致没查询到。
有时因为业务代码 数据落库 和 发MQ消息 放在一个事务,有一定概率收到消息时,事务还没提交完成。当时处理方式是收到MQ消息时sleep 1s,或许应先提交事务,完成后再发MQ消息。
但这又出来一个问题:发消息失败怎么办?后来建立本地消息表,确保MQ消息可补偿,把业务处理和保存MQ消息到本地消息表操作放在同一事务内处理,然后异步发送和补偿发送消息表中的消息到MQ。
-
先保存用户注册的数据,同时记录下要发送MQ的消息,这两个入库在一个事务
-
异步任务,定时拉取MQ的消息表,发送到MQ进行处理
这就是本地事务消息的实现,第2步不一定非得定时任务拉取到。第1步完成后直接发MQ即可, 定时任务拉取只是用来做补偿。
-
若有多个补偿实例,会不会造成消息重复? 补偿需要配合幂等,生产应用肯定用DB做幂等(如消息id)。
-
Pro发消息给MQ,即使异步发送,也会有listener来监听投递消息是否成功。若失败,也可重试。
除了使用Spring AMQP实现死信消息的重投递外,RabbitMQ 2.8.0后支持的死信交换器DLX也可以实现类似功能。
自定义的死信队列,其实是发送失败,主要是生产者发送到MQ时,发送失败,进入自定义的死信队列。 DLX是因为如下导致消息无法到达正常队列:
-
消息消费时被拒绝(basic.reject / basic.nack),且requeue = false
-
消息TTL过期
-
队列达到最大长度
-
技术分享RabbitMQ的资料 http://note.youdao.com/noteshare?id=e9f2f88c6c7fcb7ac690463eb230650a