延迟任务解决方案


一、场景说明

在许多项目中,都需要用到延迟任务,例如

  1. 文章定时发布。
  2. 外卖订单超过15分钟未支付,自动取消。
  3. 客户预定自如房子后,24小时内未支付,房源自动释放 。
  4. 使用抢票软件订到车票后,1小时内未支付,自动取消。

二、解决方案

2.1 定时任务进行DB轮询

实现方式:通过一个线程定时的扫描数据库当天创建的订单,根据订单的创建时间来判断订单是否超 时,针对超时订单进行相关的更新操作。线程定时可采用SpringTask(单机)或XXL-Job(分布式)实现。

优点:实现简单
缺点:

  • 系统订单数据量比较大,每隔1分钟轮询数据库,对服务器和数据库的内存消耗比较大。
  • 存在延迟,即使1分钟扫描一次数据库,也会存在1分钟的延迟。

2.2 监听Redis过期key

实现方式:使用Redis的Keyspace Notifications,利用key失效的提供的回调机制,处理相关的业务实 现。 修改Redis配置文件打开redis.conf 文件,搜索 “notify-keyspace-events”,修改为 “notify- keyspace-events Ex”,至此Redis 就支持Key过期事件的监听。在这里插入图片描述

@Component
public class RedisKeyExpirationListener implements MessageListener
{
    private static final Logger logger =
LoggerFactory.getLogger(RedisKeyExpirationListener.class);
    public static final String KEY_PREX = "test::order:queue";
@Override
    public void onMessage(Message message, byte[] pattern)
    {
try
        {
            String expiredKey = message.toString();
// 通过key来判断 if(!expiredKey.contains(KEY_PREX)) {
return; }
//满足条件处理具体的业务逻辑 }
        catch (Exception e)
        {
logger.error("失效事件失败",e); }
} }

优点: 基于Redis实现简单
缺点:

  • 客户端断开后重连会导致所有事件丢失。
  • 高并发场景下,存在大量的失效key场景会导致失效时间存在延迟。
  • 此方案针对业务量较少且可靠性要求不高的场景使用。

不太可靠,可能百分之九十多会成功,但是可能会失败,这种方案适合对于执行任务的可靠度没有太高要求的,可以使用。比如一些通知类型的,下单之后通知付款,通知发送是否成功对于整个系统的业务流转并没有影响,用户没有收到通知也会自己去付款。但是取消订单这种就不适合,30分钟没有付款就需要取消,这种就可靠度就必须要高。

2.3 RabbitMQ死信队列+TTL过期消息

实现方式: 用户下单后,发布一条设置过期时间的消息。消息过期后会转移到死信队列。对死信队列进行消费监听,接收到消息后找到对应订单,如果订单还未付款就更新订单状态为已取消 。
在这里插入图片描述
eg:以订单取消为例。
生产者:下单方案-保存订单-发送一条消息到mq(订单id)
rabbitmq:exchange业务交换机接收到生产者的消息,把消息交给queue队列,(队列设置一个过期时间,方法为TTL)。在设定时间之后会过期然后交给DLX死信(过期的消息称为死信)交换机(绑定之后会自动提交),死信交换机再把消息转到死信队列。
消费者:监听死信队列,消费者拿到这个死信队列过期消息中存储的订单id。
优点: 支持高并发场景消息处理
缺点:

  • 引入额外的消息队列,增加项目的维护和复杂度。
  • 支持固定时长的消息延迟,针对任意时长的消息延迟需要进行扩展。

2.4 Redis SortedSet+MySQL延迟队列

实现方式:

  • 利用Redis的SortedSet数据结构实现了文章定时发布任务的延迟队列。需要定时发布的文章,我们会写入一个发布任务记录到SortedSet中SortedSet的score中存储了文章的发布时间毫秒 值,默认会按照发布时间升序排序。另外,用XXL-Job分布式定时任务每秒从SortedSet查询小于或等于 当前时间毫秒值的记录,这些记录就是到期发布的文章任务,进而调用发布文章接口完成文章发布。
  • 考虑到该方案过于依赖Redis持久化特性来实现任务持久化,我进一步优化了方案。在把定时任 务记录写入Redis的SortedSet同时,也备份到MySQL中,防止任务丢失。
  • 这里其实还有一个问题,Redis的SortedSet如果存储数据过多,会导致SortedSet操作效率下 降。只有临近发布时间的任务数据才缓存在Redis中,其余任务先存储在MySQL。快到期的任务才使 用XXL-Job同步到Redis

在这里插入图片描述
Redis延迟微服务,把5分钟内要做的任务写入Redis,5分钟内以及5分钟以后的任务都需要写入Mysql,然后每隔5分钟Redis的任务被消费完之后,mysql补充5分钟内的新任务到Redis。(消费者全表扫描一定要扫描Redis中的任务消费)反复如此。

优点:高效可靠,适合任意时长的消息延迟 。(弥补了前三种方案的劣势)
缺点:需要额外维护一套MySQL的DB任务表。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值