应用场景,电商项目用户下单后超过指定时间未支付,订单自动失效。
使用延时队列会使用到定时任务,需要先把定时任务做好。
在用户下单成功后。定时任务定时扫描出下单成功且未支付的订单,将订单加入到延时执行队列中。同时也加入到缓存中。
延时执行类在执行订单失效时,先到缓存内查询一次,如果没有查询到,说明该订单已支付或者已取消(支付成功或取消订单清除对应缓存)
一;执行实体
package io.jboot.admin.job;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import com.hnzh.wmall.service.entity.IndentCommodity;
public class TaskEntity implements Delayed{
private int id;
private String body; //消息内容
private IndentCommodity indentCommodity;
private long excuteTime;//执行时间
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public long getExcuteTime() {
return excuteTime;
}
public void setExcuteTime(long excuteTime) {
this.excuteTime = excuteTime;
}
public TaskEntity(int id, String body,long delayTime) {
this.id = id;
this.body = body;
this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();
}
public TaskEntity(int id, IndentCommodity indentCommodity,long delayTime) {
this.id = id;
this.indentCommodity = indentCommodity;
this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();
}
public TaskEntity(int id, long delayTime) {
this.id = id;
this.excuteTime = TimeUnit.NANOSECONDS.convert(delayTime, TimeUnit.MILLISECONDS) + System.nanoTime();
}
@Override
public int compareTo(Delayed delayed) {
TaskEntity msg = (TaskEntity)delayed;
return Integer.valueOf(this.id)>Integer.valueOf(msg.id)?1:( Integer.valueOf(this.id)<Integer.valueOf(msg.id)?-1:0);
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(this.excuteTime - System.nanoTime(), TimeUnit.NANOSECONDS);
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public IndentCommodity getIndentCommodity() {
return indentCommodity;
}
public void setIndentCommodity(IndentCommodity indentCommodity) {
this.indentCommodity = indentCommodity;
}
}
二;延时执行类
package io.jboot.admin.job;
import java.sql.SQLException;
import java.util.concurrent.DelayQueue;
import com.hnzh.wmall.service.api.CommodityDetailsService;
import com.hnzh.wmall.service.api.IndentCommodityService;
import com.hnzh.wmall.service.api.IndentService;
import com.hnzh.wmall.service.entity.CommodityDetails;
import com.hnzh.wmall.service.entity.Indent;
import com.hnzh.wmall.service.entity.IndentCommodity;
import com.jfinal.log.Log;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;
import io.jboot.Jboot;
import io.jboot.admin.base.common.ServiceConst;
import io.jboot.core.rpc.Jbootrpc;
import io.jboot.core.rpc.JbootrpcServiceConfig;
public class ExecuteClass implements Runnable {
protected static final Log logger = Log.getLog(ExecuteClass.class);
// 延时队列
private DelayQueue<TaskEntity> queue;
public ExecuteClass(DelayQueue<TaskEntity> queue) {
this.queue = queue;
}
@Override
public void run() {
//获取rpc服务
Jbootrpc jbootrpc = Jboot.me().getRpc();
JbootrpcServiceConfig serviceConfig = new JbootrpcServiceConfig();
serviceConfig.setGroup(ServiceConst.SERVICE_WMALL);
CommodityDetailsService detailsService = jbootrpc.serviceObtain(CommodityDetailsService.class, serviceConfig);
IndentService IndentService = jbootrpc.serviceObtain(IndentService.class, serviceConfig);
IndentCommodityService indentCommodityService = jbootrpc.serviceObtain(IndentCommodityService.class,
serviceConfig);
while (true) {
try {
//检测该订单是否已买单 若已买单对该订单不做处理,进入下次循环
IndentCommodity indentCommodity = Jboot.me().getCache().get("DelayExecuteClass", "executeClass"+queue.take().getIndentCommodity().getIndentNumber());
//如果缓存中没取到订单对象,表示该订单已支付,或已取消(支付成功时在缓存中清除对应订单)
if(indentCommodity == null){
continue;
}
TaskEntity take = queue.take();
//业务开始 订单状态开始执行更新
//更新完毕后从缓存中清除该订单号
Jboot.me().getCache().remove("DelayExecuteClass", "executeClass"+take.getIndentCommodity().getIndentNumber());
logger.debug("订单号为>>>"+commodity.getIndentNumber() +"的订单过期状态更新完毕,cache内已清除");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
三;在定时任务执行类中,将所有需要加入到延时执行队列中的订单加入进去。注:定时任务下次更出来吧
// 初始化订单延时队列计数器
private static int counter = 0;
/**
* 订单超时定时任务,若超时,则将该订单数据更新
*
* @param minutes
* @return
*/
public synchronized String indentTimeout(String minutes) {
//获取rpc服务
Jbootrpc jbootrpc = Jboot.me().getRpc();
JbootrpcServiceConfig serviceConfig = new JbootrpcServiceConfig();
serviceConfig.setGroup(ServiceConst.SERVICE_WMALL);
IndentCommodityService indentCommodityService = jbootrpc.serviceObtain(IndentCommodityService.class,
serviceConfig);
// 查询出所有未支付订单
List<IndentCommodity> commodities = indentCommodityService.findIndentCommodity(null);
for (int i = 0; i < commodities.size(); i++) {
counter++;
// 延时执行时间 转为毫秒
long time = Integer.parseInt(minutes) * 60 * 1000;
long delayTime = time - (new Date().getTime() - commodities.get(i).getCreateTime().getTime());
TaskEntity te = null;
if (delayTime < 0) {
logger.debug("超过" + minutes + "分钟未加入到队列,订单号>>>" + commodities.get(i).getIndentNumber() + ",立即加入队列");
te = new TaskEntity(counter, commodities.get(i), 0);
} else {
te = new TaskEntity(counter, commodities.get(i), delayTime);
// 检测该订单对象是否在延时队列中,若存在不做处理
IndentCommodity indentCommodity = Jboot.me().getCache().get("DelayExecuteClass",
"executeClass" + commodities.get(i).getIndentNumber());
if (indentCommodity != null) {
continue;
}
}
// 将需处理的订单对象加入到缓存内
Jboot.me().getCache().put("DelayExecuteClass", "executeClass" + commodities.get(i).getIndentNumber(),
commodities.get(i));
// 将需处理的订单对象加入到延时队列中
queue.offer(te);
}
return "success";
}