Redis 过期队列

363 阅读1分钟

应用场景:

1、订单成功后,在30分钟内没有支付,自动取消订单

2、外卖平台发送订餐通知,下单成功后60s给用户推送短信

3、如果订单一直处于某一个未完结状态时,及时处理关单,并退还库存

实现思路:

具体代码:

// 初始定时任务线程池 
@Componentpublic class ScheduledExecutor { 
  private static final Logger logger = LoggerFactory.getLogger(ScheduledExecutor.class); 
  private ScheduledThreadPoolExecutor executor;

  public void execute(Runnable runnable, long delay) {  
    executor.schedule(runnable, delay, TimeUnit.MILLISECONDS);  
 }  
 @PostConstruct  
 public void init() {  
    // 初始化线程池  
    executor = new ScheduledThreadPoolExecutor(10);   
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());  
 }    
  @PreDestroy 
  public void destroy() { 
     executor.shutdown(); 
     logger.info("ServerExecutor-destroy():threadExecutor:销毁");  
 }
}


// 任务执行线程池
@Component
public class ScheduledExecutor {
    private static final Logger logger = LoggerFactory.getLogger(ScheduledExecutor.class);
    private ScheduledThreadPoolExecutor executor;
    public void execute(Runnable runnable, long delay) { 
          executor.schedule(runnable, delay, TimeUnit.MILLISECONDS);
    }

    @PostConstruct
    public void init() {
        // 初始化线程池  
         executor = new ScheduledThreadPoolExecutor(10);
         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    }

    @PreDestroy
    public void destroy() {
       executor.shutdown();  
       logger.info("ServerExecutor-destroy():threadExecutor:销毁");
    }
}


// service 
@Slf4j
@Component
public class RedisServiceTest{

    @Autowiredprivate RedisUtil redisUtil;

    @Autowiredprivate ScheduledExecutor schedule;

    @Autowiredprivate CachedExecutor cachedExecutor;

    private static AtomicBoolean exists = new AtomicBoolean(true);


    @PostConstruct
    public void init() {  
         try {      
            cachedExecutor.executeTask(() -> {    
               // 1:初始化从redis中获取到最近待待失效订单     
                log.debug("开始初始化============"); 
                TypedTuple<Object> tupleSet = popOrder(); 
                // 2:scheduleTask delay execute该订单    
                 executeTask(tupleSet);     
                 });   
            } catch (Exception e) {   
               // TODO: handle exception  
                e.printStackTrace(); 
            }
    }

    @PreDestroy
    public void destroy() {  
         exists.set(false);
    }

/**
    * 实例 scheduled
    *
    * @param tupleSet
    */
   public void executeTask(TypedTuple<Object> tupleSet) {
      if (null != tupleSet) {
         Long expireTime = Double.valueOf(tupleSet.getScore()).longValue();
         long delayTime = expireTime - System.currentTimeMillis();
         // 实例taskTrack
         schedule.execute(() -> {
            TypedTuple<Object> tuple = popOrder();
            executeTask(tuple);
         }, delayTime);
      }
   }


/**
   * 1:尝试消费 2:实例新的scheduled
   *
   * @return
   */
  public TypedTuple<Object> popOrder() {
    TypedTuple<Object> tupleSet = null;
    // 
    while (exists.get()) {
      // 获取队列头数据
      tupleSet = redisUtil.zrangeWithScore("Test:Order:ToPay");
      if (null == tupleSet) {
        try {
          Thread.sleep(2000);
          continue;
        } catch (Exception e) {
          log.error("{}", e);
        }
      }
      Long expireTimeLong = Double.valueOf(tupleSet.getScore()).longValue();
      if (System.currentTimeMillis() >= expireTimeLong) {
        String orderId = tupleSet.getValue().toString();
        // TODO 处理过期数据 然后移除队列 这里要注意 多节点处理数据时 不安全问题 利用redis单线程 删除成功方可操作订单 
      } else {
        // 如果存在待失效则弹出
        log.debug("存在待失效============");
        break;
      }
    }
    return tupleSet;
  }



}