延迟队列技术方案
什么场景需要使用延迟队列
1、订单成功后,在30分钟内没有支付,自动取消订单
2、外卖平台发送订餐通知,下单成功后60s给用户推送短信。
3、如果订单一直处于某一个未完结状态时,及时处理关单,并退还库存
4、淘宝新建商户一个月内还没上传商品信息,将冻结商铺等
我们的需求
- 最长每三天执行一次,最短的1s(iap支付,续费重试)
(https://yuque.antfin.com/tp_team/ohzygl/fx4caw) - 高可用、不允许数据丢失
- 对实时性要求不高,允许延迟误差
- 允许重复消费(服务方做幂等)
- 需要监控(队列堆积)
- 可查询延迟队列单key的消费情况
现有方案
-
JDK自带的DelayQueue(不适合我们目前的场景)
-
定时任务 + DB + Redis
-
Redis sortedSet
-
Reids过期回掉(不是很靠谱)
-
RocketMQ (1s/5s/10s/30s/1m/2m/3m/4m/5m/6m/7m/8m/9m/10m/20m/30m/1h/2h)
解决定时的方案: 根据level,消费时重新投递。
存在在db,定时消费。 -
RabbitMQ(TTL & DLX)
-
Netty 时间轮
目前开源的延迟消息,也就pulsar比较可靠,其次是rocketmq,但rocket只支持18个等级的固定刻度,pulsar也是近几天内的任意时间,长时间不行。其他的,rabbit不适合大量堆积,redis key多的时候会严重滞后(惰性删除),而且没有ack。sorted set,在field超过一定量时预计5000左右会产生性能问题,需要做二次分片。jdk的delayqueue,性能就不提了,只能做短时、量少的延迟消息,而且还没有ack,可能存在丢失,如果要写入数据库,也很麻烦。内存时间轮性能好点,但也没法ack,而且消费阻塞会引起问题(单线程)
目前最佳实践就是组合以上的几种,定时任务+pulsar/rocket本身时间轮等,多重组合,可以实现海量,超长时间的延迟消息(不修改几种组件源码的情况下)
参考链接:
https://www.cnblogs.com/rickiyang/p/12237612.html
https://juejin.cn/post/6844904150703013901