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());
}
}
}