定义
延迟队列存储的对象肯定是对应的延时消息,所谓"延时消息"是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费。
定时任务缺陷
业务需要处理订单和库存的数据一致性,有以下实现方案
1)使用seata实现分布式事务控制,但是高并发下性能降低。
2)使用spring的schedule定时任务轮询数据库
3)使用定时任务实现订单自动关闭(30min未支付)
缺点:消耗系统内存(一直轮询,定期扫描)增加了数据库的压力(每隔一段时间就要做全表扫描) 存在较大的时间误差,也就是定时任务失效问题,不能保证时效性。
比如有以下场景:假如开启了一个每隔30分钟定时任务,0分开始,用于扫描订单过期的,假如有一个订单1分钟下单了,那就在31分才过期,但是上一次定时任务在30分执行了,所以没法被扫描到,这个订单就只能等到下一次定时任务才能被执行,而下一次要到60分,所以导致这过期订单等了29分才被关闭。
最终解决方案:延时队列(延时任务,kafka,rabbitmq等)
rabbitmq可以通过设置队列的TTL和死信路由实现延迟队列
TTL(2种方式设置消息的有效期):
RabbitMQ可以针对消息属性设置x-expires 或者 针对队列属性设置 x-message-ttl,来控制消息的生存时间,如果超时(两者同时设置以最先到期的时间为准),则消息变为dead letter(死信)
推荐:队列属性设置 x-message-ttl
如果消息属性设置x-expire,由于RabbitMQ队列是先进先出,并且RabbitMQ采用的惰性检查,比如先后存了三个消息过期时间分别为5min,3min,1min,RabbitMq会先拿到5min这个消息,结果发现要5分钟之后才过期,结果放回队列,导致后面的消息3min,1min一直还在。
死信路由DLX
RabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由转发到指定的队列。
x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchange
x-dead-letter-routing-key:出现dead letter之后将dead letter重新按照指定的routing-key发送
RabbitMQ延时队列
一般个服务指定一个交换机,但是绑定多个队列,针对订单模块创建以上消息队列,创建订单时消息会被发送至队列order.delay.queue,经过TTL的时间后消息会变成死信以order.release.order的路由键经交换机转发至队列order.release.order.queue,再通过监听该队列的消息来实现过期订单的处理。
* 如果该订单已支付,则无需处理
* 否则说明该订单已过期,修改该订单的状态并通过路由键order.release.other发送消息至队列 stock.release.stock.queue进行库存解锁
注意:orderDelayQueue队列是一个特殊队列,不能有消费者监听,否则消息会被立即消费,做不到延迟的效果。
我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!