编写了个简单的电商下单 延时取消的示例
Delayed
public class Delayed implements java.util.concurrent.Delayed {
/**
* 记录id 订单id
*/
private Integer id;
public Integer getId() {
return id;
}
/**
* 开始时间
*/
private long start = System.currentTimeMillis();
/**
* 多少秒后到期
*/
private long time ;
public Delayed(Integer id,long time) {
this.id = id;
this.time = time;
}
/**
*方法中,返回与此对象关联的剩余延迟
*给定时间单位
*
* @param unit 时间单位
* @return 剩余延迟;零或负值表示
* that the delay has already elapsed
*/
@Override
public long getDelay(TimeUnit unit) {
return unit.convert((start+time) - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
/**
* 用于延迟队列内部比较排序 当前时间的延迟时间 - 比较对象的延迟时间
* @param o
* @return
*/
@Override
public int compareTo(java.util.concurrent.Delayed o) {
Delayed o1 = (Delayed) o;
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
@Override
public String toString() {
return "OrderDelayed{" +
", start=" + start +
", id=" + id +
", time=" + time +
'}';
}
}
DelayedVo
@Data
@ToString
public class DelayedVo implements Serializable {
/**
* 记录id 订单id
*/
private Integer id;
/**
* 需要倒计时时间挫
*/
private Long time;
}
DelayQueue
@Slf4j
@Component
public class XiaoDelayQueue implements CommandLineRunner {
/**
* 线程池核心线程数
*/
private static final int COREPOOLSIZE = 10;
/**
* 最大线程数
*/
private static final int MAXPOOLSIZE = 10;
/**
* 保存最大时间
*/
private static final int KEEPALIVESECONDS = 60;
/**
* 最大延时队列
*/
private static final int DELAYQUEUESIZE = 1000;
private static final String ORDER = "goods_time";
@Resource
private RedisUtil redisUtil;
@Resource
private OrderService orderService;
private DelayQueue<Delayed> delayQueue = new DelayQueue();
private ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
/**
* 加入到延时队列中
*
* @param delayed
*/
public void put(Delayed delayed) {
log.info("加入延时任务:{}", delayed);
delayQueue.put(delayed);
}
/**
* 取消延时任务
*
* @param delayed
* @return
*/
public boolean remove(Delayed delayed) {
log.info("取消延时任务:{}", delayed);
return delayQueue.remove(delayed);
}
public void getDelayed() {
log.info("开始获取延时任务");
while (true) {
//加数量限制 可以让其它微服务分担
if (delayQueue.size() <= DELAYQUEUESIZE) {
Object o = redisUtil.rightPop(ORDER);
if (!StringUtils.isEmpty(o)) {
log.info("延时任务获取====>{}", o.toString());
DelayedVo delayedVo = JsonUtil.fromJson(o.toString(), DelayedVo.class);
put(new Delayed(delayedVo.getId(), delayedVo.getTime()));
}
}
}
}
@Override
public void run(String... args) {
log.info("初始化Executor");
asyncServiceExecutor();
log.info("初始化getDelayed");
executor.execute(new Thread(this::getDelayed));
//多线程执行
for (int i = 0; i <= COREPOOLSIZE - 1; i++) {
log.info("初始化excuteThread");
executor.execute(new Thread(this::excuteThread));
}
}
private Executor asyncServiceExecutor() {
// 设置核心线程数
executor.setCorePoolSize(COREPOOLSIZE);
// 设置最大线程数
executor.setMaxPoolSize(MAXPOOLSIZE);
//配置队列大小
executor.setQueueCapacity(Integer.MAX_VALUE);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(KEEPALIVESECONDS);
// 设置默认线程名称
executor.setThreadNamePrefix("订单延时检查线程池");
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
//执行初始化
executor.initialize();
return executor;
}
/**
* 延时任务执行线程
*/
public void excuteThread() {
log.info("执行延时任务");
while (true) {
try {
Delayed delayed = delayQueue.take();
cancelOrderInfo(delayed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 队列更改状态
*
* @param delayed
*/
private void cancelOrderInfo(Delayed delayed) {
log.info("开始执行延时任务");
//调用取消订单方法
orderService.cancelOrder(delayed.getId());
}
}
OrderService
public interface OrderService {
/**
* 下单
* @param placeAnOrderBo
* @param userTokenVo
* @return
*/
Object placeAnOrder(String goodsId, String userId) throws IOException;
/**
* 队列取消订单
* @param id
*/
void cancelOrder(Integer id);
/**
* 手动取消订单
* @param orderId
* @return
*/
Object manualCancelOrder(Integer orderId);
}
OrderServiceImpl
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private DelayQueue delayQueue;
@Resource
private RedisUtil redisUtill;
private static final String ORDER = "goods_time";
@Override
public Object placeAnOrder(String gameId, String userId) throws IOException {
UserInfo userInfo = userInfoService.getById(userId);
GoodsInfo goodsInfo = goodsInfoService.getById(gameId);
//生成订单
OrderRecord orderRecord = new OrderRecord();
orderRecord.setUserId(userInfo.getUserId());
orderRecord.setGoodsId(goodsInfo.getGoodsId());
Long orderNo = System.currentTimeMillis();
orderRecord.setOrderCode(userInfo.getUserId() +orderNo.toString());
//15分钟
long time = 60 * 1000 * 15;
//计算过期时间
long expiration = date.getTime() + time;
//保存过期时间
orderRecord.setExpiration(new Date(expiration));
// 1.未付款 2.未发货 3.失效 4.已发货 5.取消订单
orderRecord.setOrderStates(DealConstant.ORDER_STATUS_1);
orderRecordMapper.insert(orderRecord);
//加入队列
Push(orderRecord.getOrderId(),time);
return "下单成功";
}
@Override
public void cancelOrder(Integer id) {
OrderRecord orderRecord = orderRecordMapper.selectById(id);
log.info("id:{}",id);
if (orderRecord.getOrderStates() != DealConstant.ORDER_STATUS_1){
return "订单已操作";
}
Integer states = orderRecord.getOrderStates();
//更改订单状态
// 1.未付款 2.未发货 3.失效 4.已发货 5.取消订单
orderRecord.setOrderStates(DealConstant.ORDER_STATUS_3);
log.info("目前状态:{}",states);
orderRecordMapper.update(orderRecord,new LambdaQueryWrapper<OrderRecord>()
.eq(OrderRecord :: getOrderId,id)
.eq(OrderRecord :: getOrderStates , states));
log.info("id:{}",orderRecord.getOrderId());
log.info("修改后状态:{}",orderRecord.getOrderStates());
}
@Override
public Object manualCancelOrder(Integer orderId) {
OrderRecord orderRecord = orderRecordMapper.selectById(orderId);
//更改订单状态
// 1.未付款 2.未发货 3.失效 4.已发货 5.取消订单
orderRecord.setOrderStates(DealConstant.ORDER_STATUS_5);
orderRecordMapper.updateById(orderRecord);
long time = orderRecord.getExpiration().getTime() - date.getTime();
LRemove(orderId,time);
return "取消成功";
}
/**
* 加入队列
*
* @param id
* @param time
*/
private void Push(Integer id, long time) {
DelayedVo delayedVo = new DelayedVo();
delayedVo.setId(id);
delayedVo.setTime(time);
log.info("加入队列:{},》》》{}",delayedVo.getTime(),id);
redisUtill.leftPush(ORDER, JsonUtil.toJson(delayedVo));
}
/**
* 移除队列
*
* @param id
* @param time
*/
private void LRemove(Integer id, long time) {
DelayedVo delayedVo = new DelayedVo();
//计算延迟时间
delayedVo.setId(id);
delayedVo.setTime(time);
redisUtill.lRemove(ORDER, -1, JsonUtil.toJson(delayedVo));
xiaoDelayQueue.remove(new Delayed( id, time));
}
}