redis使用zet实现延迟队列

service层代码
  // 这里暂时用spring的@Async,后续改成用evcall
    // 将 已提交 的订单放入延迟队列,超时未支付则自动进行订单的 取消 操作,异步操作
    Jedis jedis = jedisPool.getResource();
    // 创建订单信息对象
    JSONObject orderInfo = new JSONObject();
    orderInfo.put("tenant", tenant);
    orderInfo.put("flowNo", flowNo);
    // 将订单信息放入延迟队列
    autoCancelOrder.delay(jedis, ORDER_PAY_TIMEOUT_KEY, orderInfo.toJSONString(), ORDER_PAY_TIMEOUT_TIME * 60 * 1000);
    autoCancelOrder.loop(jedis, ORDER_PAY_TIMEOUT_KEY);

redis层代码: 

package com.hd123.baas.das.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.hd123.baas.das.service.api.order.Order;
import com.hd123.baas.das.service.api.order.OrderService;
import com.hd123.baas.das.service.api.order_state.OrderState;
import com.hd123.baas.das.service.dao.order.OrderDao;
import com.hd123.baas.das.service.dao.order_state.OrderStateDao;
import com.hd123.baas.das.service.enums.OrderStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import java.lang.reflect.Type;
import java.util.Set;
import java.util.UUID;

/**
 * 自动取消订单
 * 
 * @param <T>
 */
@Service
@Slf4j
@EnableAsync
public class AutoCancelOrder<T> {

  @Autowired
  private OrderDao orderDao;
  @Autowired
  private OrderStateDao orderStateDao;
  @Autowired
  private OrderService orderService;

  static class TaskItem<T> {
    public String id;
    public T msg;
  }

  // fastjson序列化对象中存在generic类型时,需要使用TypeReference
  private Type TaskType = new TypeReference<TaskItem<T>>() {
  }.getType();

  /**
   * 将指定延迟消费的消息放入延时队列
   * 
   * @param msg
   */
  public void delay(Jedis jedis, String queueKey, T msg, long outTime) {
    TaskItem<T> task = new TaskItem<T>();
    // 分配唯一的uuid
    task.id = UUID.randomUUID().toString();
    task.msg = msg;
    // fastjson 序列化
    String s = JSON.toJSONString(task);
    // 塞入延时队列, outTime 后再试
    jedis.zadd(queueKey, System.currentTimeMillis() + outTime, s);
  }

  /**
   * 轮训zset获取到期的任务进行处理
   */
  @Async("taskExecutor")
  public void loop(Jedis jedis, String queueKey) {
    while (!Thread.interrupted()) {
      // 只取一条
      Set<String> values = jedis.zrangeByScore(queueKey, 0, System.currentTimeMillis(), 0, 1);
      if (values.isEmpty()) {
        try {
          // 歇会继续
          Thread.sleep(500);
        } catch (InterruptedException e) {
          break;
        }
        continue;
      }
      String s = values.iterator().next();
      // 抢到了并移除该元素
      if (jedis.zrem(queueKey, s) > 0) {
        // fastjson反序列化
        TaskItem<T> task = JSON.parseObject(s, TaskType);
        this.handleMsg(task.msg);
      }
    }
  }

  /**
   * 消费消息
   * 
   * @param msg
   */
  private void handleMsg(T msg) {
    // 将订单信息的json字符串转成json对象
    JSONObject orderInfo = JSONObject.parseObject((String) msg);
    String tenant = orderInfo.getString("tenant");
    String flowNo = orderInfo.getString("flowNo");
    // 根据 tenant+flowNo 得到该订单
    Order order = orderDao.getByTenantAndFlowNo(tenant, flowNo);
    // 判断该订单的状态
    // 如果仍然为已提交(OrderStatusEnum.SUBMITTED),则将该订单状态自动修改为已取消(OrderStatusEnum.PAID)
    OrderState dbOrderState = orderStateDao.findByTenantAndOwner(tenant, order.getUuid());
    OrderStatusEnum dbState = dbOrderState.getState();
    if (OrderStatusEnum.SUBMITTED == dbState) {
      try {
        orderService.cancel(tenant, flowNo, "订单超时未支付自动取消订单");
      } catch (Exception e) {
        log.error("订单自动取消失败,flowNo={}", flowNo);
        e.printStackTrace();
      }
    }
  }
}

配置线程池:

package com.hd123.baas.das.configuration;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

    @Override
    @Bean("taskExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心数
        executor.setCorePoolSize(20);
        // 最大数
        executor.setMaxPoolSize(50);
        // 队列大小
        executor.setQueueCapacity(200);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("-TaskExecutor-");
        // 拒绝策略 任务提交线程自身执行,不会拒绝抛异常导致任务失败
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    // 配置线程池执行异常的处理类(当多线程任务本身无catch处理时才抛给这个类进行处理)
    // AsyncConfigurer 接口中的这个方法用于返回异常时的处理类,就是上面自己定义的类
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return new MyAsyncExceptionHandler();
    }

    // 定义一个实现AsyncUncaughtExceptionHandler 接口的类,自定义多线程任务执行异常的处理逻辑
    class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
        @Override
        public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
           log.error("Exception in async method:"+ throwable.getMessage());
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值