RabbitMQ延时队列
1、场景:
比如未付款订单,超过一定时间后,系统自动取消订单,并释放占用物品
2、上述场景的解决方案:
1)spring schedule的定时任务轮询数据库
缺点:消耗系统内存,增加数据库压力,存在较大的时间误差
(例如如果订单30分钟未支付时取消订单,且定时任务30分钟执行一次,那么一个订单最晚会59分钟才会被定时任务扫描到已取消,例如定时任务在0分钟时执行,恰巧订单在一分钟时来了,定时任务在30分钟时扫描订单,发现订单还没过期,但是31分钟订单恰好过期,于是等到60分钟,定时任务再次扫描时,才能扫描到这个过期订单,所以订单在一分钟被创建,60分钟才被发现过期)
2)RabbitMQ延时队列
rabbitmq延时队列 (消息ttl和死信Exchange结合)
3、消息的TTL(time to live)
消息的ttl就是消息的存活时间
RabbitMQ可以对队列和消息分别设置TTL
对队列设置ttl就是,消息在队列的逗留时间;也可以对每一个单独的消息做单独的设置。超过了这个时间,我们就认为这个消息死了,称之为死信
如果队列设置了ttl,消息也设置了,会取两者之间较小的。可以通过设置消息的expiration字段或者x-message-ttl属性来设置时间,两者是一样的效果
4、Dead Letter Exchange (DLX)
5、延时队列的实现
设置队列过期时间实现延时队列 (推荐)
设置消息过期时间来实现延时队列
6、延时队列定时关闭订单模拟
交换机,队列以及路由键的相关情况如下:
p消息,经过交换机order-event-exchange路由到延时队列order.delay.queue,延时队列设置了如上三个属性,当延时队列中的消息过期后,会转到指定的Dead Letter Exchage(order-event-exchange),Dead Letter Exchage在将消息路由到指定的队列进行处理
7、消息丢失、积压、重复的解决方案
如何保证消息的可靠性
1、消息丢失
消息发送出去,由于网络原因没有到达服务器(producer到broker)
做好容错方法(try catch),发送消息可能会网络失败,失败后要有重试机制,科技路到数据库,采用定期扫描重发的方式
做好日志记录,每个消息是否被服务器收到,应该记录
做好定期重发,定期去数据库扫描未成功发送的消息进行重发
消息抵达Broker,Broker要将消息写入磁盘(持久化)才算成功。此时Broker尚 未持久化完成,宕机
publisher也必须加入确认回调机制,确认成功的消息,修改数据库消息状态
自动ACK的状态下。消费者收到消息,但没来得及消息然后宕机
一定开启手动ACK,消费成功才移除,失败或者没来得及处理就noAck并重新入队
2、消息重复
消息消费成功,事务已经提交,ack时,机器宕机。导致没有ack成功,Broker的消息 重新由unack变为ready,并发送给其他消费者(当消息正在被消费者消费时,消息是unack状态,当消息被reject时,由unack变为ready;当消息被正常消费时,从队列移除)
消费者的业务消费接口应该设计为幂等性的。比如扣库存有 工作单的状态标志
使用防重表(redis/mysql),发送消息每一个都有业务的唯 一标识,处理过就不用处理
rabbitMQ的每一个消息都有redelivered字段,可以获取是否 是被重新投递过来的,而不是第一次投递过来的
3、消息积压
消费者宕机积压
消费者消费能力不足积压
发送者发送流量太大
上线更多的消费者,进行正常消费
上线专门的队列消费服务,将消息先批量取出来,记录数据库,离线慢慢处理