实现一个基于Redis的简单的延时队列(rbsdq-spring-boot-starter)

概述

项目中常常需要延时触发一些操作,比如订单30min没有付款就取消订单、有些金融机构的支付接口退款没有退款通知,需要主动调用查询接口等等。这些场景有如下特点:

  • 吞吐量要求不大
  • 一般的消费逻辑都是查询更新或更改一条数据,对于消息重复发送/消费并不严格
  • 消息消费一次后可能还需要重新加入队列再次消费(延时退款查询状态仍为退款中,需要再次查询)

基于以上几点,可以尝试用Redis的zset数据结构来实现一个延时队列(Redis Based SImple Delat queue)

接口设计

在这里插入图片描述

  • BaseDelayQueue是一个抽象类,继承自Thread类,重写了run()方法,实现了消费流程;还定义了一个抽象方法job(),同于对外提供消费逻辑的实现,返回值为布尔值,表示消息是否要删除,为false则消息的score会被置为当前时间戳从而放到队尾。其还包含两个属性DeleyQueueConfig和ExceptionStrategy,分别用于消费参数配置和消费异常相关的配置。
  • BaseQueueConfig是一个抽象类,两个属性分别指定了redis的key和一个template对象用于消费/异常处理相关的操作
  • DeleyQueueConfig队列配置,继承自BaseQueueConfig。主要参数有消息延时时长delay和线程最大休眠时长maxIdle。
  • ExceptionStrategy是异常配置策略,定义了一个属性openExceptionList指定是否开启异常集合。
  • QueueFactory是一个工厂类,其聚合了客户端定义的所有的队列实例,实现简单的管理。

消费流程

在这里插入图片描述
  其中线程休眠时长delay指定队列的延时消费时间,maxIdle参数指定线程最大休眠时间,ExceptionStrategy对象的openExceptionList属性指定是否开启异常集合。当队列中没有消息或者消息未到消费时间,则线程会进入休眠,休眠时长计算规则如下:

  1. 队列中无消息,则休眠时长为maxIdle
  2. 若消息没到消费时间,时间差为diff(<delay),需要休眠:如果diff>maxIdle,则休眠时长为maxIdle;反之休眠时间为diff;

可以通过减小maxIdle的值来提高队列消费的时效性,但这样在空队列的情况下可能导致频繁访问redis,影响性能。具体根据需要设置合适的值。

DEMO

  starter写好后,就可以引入项目中进行使用了

  1. 创建一个延时队列类RefundUpdateQueue,继承BaseDelayQueue抽象类;由于BaseDelayQueue中没有定义无参构造,所以先要为RefundUpdateQueue类增减一个带有DelayQueueConfig类型参数的有参构造;
  2. 实现job方法完成消费逻辑,返回Boolean值指定消息消费完是否删除
  3. 将RefundUpdateQueue对象加载到spring容器中
public class RefundUpdateQueue extends BaseDelayQueue {


    private RefundOrderService refundOrderService;

    public RefundUpdateQueue (DelayQueueConfig config, RefundOrderService refundOrderService){
        super(config);
        this.refundOrderService = refundOrderService;
    }


    @Override
    protected Boolean job(String value) {
        BankRefundOrderStatus status = refundOrderService.validateRefund(value,null,null).getStatus();
        return status != BankRefundOrderStatus.REFUNDING;
    }
}



@Configuration
public class DelayQueueRegistry{
    @Bean
    public RefundUpdateQueue refundUpdateQueue(StringRedisTemplate stringRedisTemplate, RefundOrderService refundOrderService){
        DelayQueueConfig config = new DelayQueueConfig(RedisComponent.REFUND_UPDATE_QUEUE,stringRedisTemplate,60*1000,1000*60*5,
                new ExceptionStrategy(RedisComponent.REFUND_UPDATE_EX_MAP,stringRedisTemplate));
        return  new RefundUpdateQueue(config,refundOrderService);
    }
}

  这里可以看到job方法是校验退款是否已经完成(成功/失败),如果退款状态为退款中,返回false下次继续校验;注意这里的QueueFactory已经在starter定义好了,只要定义了BaseDelayQueue类型的bean,其会自动被加入到QueueFactory中,线程被启动。

不足

  • 只支持string类型的值,作为队列的值
  • 在空队列时会进性线程休眠,这样可能会存在一定的消费时间误差;如果一味的减小maxIdle的值,虽然消费时间的误差可能会减小,返回造成频繁的访问redis,消耗过多资源;具体的参数根据需要来定
  • 目前还未解决分布式情况下,多消费者并发消费同一队列的问题;初步想法时,各消费者对队列进行争抢上锁,抢到拿到队列一定时间段的消费权,到时间各消费者重新争抢上锁。
      针对以上几点,使用场景大致可以限定在:支付、退款相关的延时查询、订单的延时取消等查询更新场景。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
shiro-redis-spring-boot-starter一个用于集成Apache Shiro和Redis的Spring Boot Starter项目。Apache Shiro是一个强大而灵活的Java安全框架,用于身份验证、授权和会话管理等安全功能。而Redis一个高性能的内存数据库,其具有快速的数据存取能力和持久化支持。 shiro-redis-spring-boot-starter提供了一种简化和快速集成Shiro和Redis的方式,使得在Spring Boot应用中实现安全功能变得更加容易。通过使用该Starter,我们可以方便地将Shiro的会话管理功能存储到Redis中,从而支持分布式环境下的会话共享和管理。 使用shiro-redis-spring-boot-starter可以带来以下好处: 1. 分布式环境的会话共享:通过将Shiro的会话数据存储到Redis中,不同的应用节点可以共享同一个会话,从而实现分布式环境下的会话管理和跨节点的身份验证和授权。 2. 高可用性和性能:Redis作为一个高性能的内存数据库,具有出色的数据读写能力和持久化支持,可以提供可靠的会话存储和高性能的数据访问能力。 3. 简化配置和集成:shiro-redis-spring-boot-starter提供了封装好的配置和集成方式,减少了我们自己实现集成的复杂性和工作量。 总结来说,shiro-redis-spring-boot-starter为我们提供了一种简化和快速集成Shiro和Redis的方式,使得在Spring Boot应用中实现安全功能变得更加容易和高效。通过它,我们可以实现分布式环境下的会话共享和管理,提供高可用性和性能的数据存取能力,同时简化了配置和集成的复杂性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值