Java中分布式事务补偿机制,当A服务调用B服务失败时,使用该异步注解则,会把失败调用数据保存到数据库中,进行重试,从而保证B服务调用成功,即使调用不成功,也可以拿到报错信息,留下对应的调用记录,代码如下:
annotation:
package com.lx.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
*类描述:异步任务注解
*
*@Author:liuweiping
*@Version:1.0.0
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface AsyncTask {
/**
* 功能描述
*/
String value() default "";
/**
*
* 延迟执行,当需要当前事务提交后才执行可设置
*/
long delay() default 3000;
/**
* 过滤重复参数任务
*/
boolean filterRepeatCall() default true;
}
entiry:
package com.lx.task.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lx.conmon.BaseEntity;
import java.util.Date;
/**
*
*类描述:异步任务表
*
*@Author:liuweiping
*@date:2021年07月09日
*@Version:1.0.0
*/
@TableName("t_async_task")
public class AsyncTaskEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(type=IdType.AUTO)
private Integer id;
/**
* 仓库编码
*/
private String locno;
/**
* 任务名称
*/
private String name;
/**
* 业务ID
*/
private String sid;
/**
* 类
*/
private String execBean;
/**
* 方法
*/
private String execMethod;
/**
* 参数
*/
private String execParams;
/**
* 执行结果
*/
private String execResult;
/**
* 执行签名MD5(类,方法,参数)
*/
private String execSign;
/**
* 执行时间
*/
private Integer execTime;
/**
* 执行次数
*/
private Integer tryCount;
/**
* 是否执行成功(0:否,1:是)
*/
private Integer successFlag;
/**
* 首次执行时间
*/
private Date firstTime;
/**
* 最后执行时间
*/
private Date lastTime;
/**
* 下次重试时间
*/
private Date nextTime;
/**
* 备注
*/
private String remarks;
/**
* 获取:ID
*/
public Integer getId() {
return id;
}
/**
* 设置:ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 获取:仓库编码
*/
public String getLocno() {
return locno;
}
/**
* 设置:仓库编码
*/
public void setLocno(String locno) {
this.locno = locno;
}
/**
* 获取:任务名称
*/
public String getName() {
return name;
}
/**
* 设置:任务名称
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取:业务ID
*/
public String getSid() {
return sid;
}
/**
* 设置:业务ID
*/
public void setSid(String sid) {
this.sid = sid;
}
/**
* 获取:类
*/
public String getExecBean() {
return execBean;
}
/**
* 设置:类
*/
public void setExecBean(String execBean) {
this.execBean = execBean;
}
/**
* 获取:方法
*/
public String getExecMethod() {
return execMethod;
}
/**
* 设置:方法
*/
public void setExecMethod(String execMethod) {
this.execMethod = execMethod;
}
/**
* 获取:参数
*/
public String getExecParams() {
return execParams;
}
/**
* 设置:参数
*/
public void setExecParams(String execParams) {
this.execParams = execParams;
}
/**
* 获取:执行结果
*/
public String getExecResult() {
return execResult;
}
/**
* 设置:执行结果
*/
public void setExecResult(String execResult) {
this.execResult = execResult;
}
/**
* 获取:执行签名MD5(类,方法,参数)
*/
public String getExecSign() {
return execSign;
}
/**
* 设置:执行签名MD5(类,方法,参数)
*/
public void setExecSign(String execSign) {
this.execSign = execSign;
}
/**
* 获取:执行时间
*/
public Integer getExecTime() {
return execTime;
}
/**
* 设置:执行时间
*/
public void setExecTime(Integer execTime) {
this.execTime = execTime;
}
/**
* 获取:执行次数
*/
public Integer getTryCount() {
return tryCount;
}
/**
* 设置:执行次数
*/
public void setTryCount(Integer tryCount) {
this.tryCount = tryCount;
}
/**
* 获取:是否执行成功(0:否,1:是)
*/
public Integer getSuccessFlag() {
return successFlag;
}
/**
* 设置:是否执行成功(0:否,1:是)
*/
public void setSuccessFlag(Integer successFlag) {
this.successFlag = successFlag;
}
/**
* 获取:首次执行时间
*/
public Date getFirstTime() {
return firstTime;
}
/**
* 设置:首次执行时间
*/
public void setFirstTime(Date firstTime) {
this.firstTime = firstTime;
}
/**
* 获取:最后执行时间
*/
public Date getLastTime() {
return lastTime;
}
/**
* 设置:最后执行时间
*/
public void setLastTime(Date lastTime) {
this.lastTime = lastTime;
}
/**
* 获取:下次重试时间
*/
public Date getNextTime() {
return nextTime;
}
/**
* 设置:下次重试时间
*/
public void setNextTime(Date nextTime) {
this.nextTime = nextTime;
}
/**
* 获取:备注
*/
public String getRemarks() {
return remarks;
}
/**
* 设置:备注
*/
public void setRemarks(String remarks) {
this.remarks = remarks;
}
}
package com.lx.task.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.lx.conmon.BaseEntity;
/**
*
*类描述:异步任务历史表
*
*@Author:liuweiping
*@date:2021年08月12日
*@Version:1.0.0
*/
@TableName("t_async_task_history")
public class AsyncTaskHistoryEntity extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(type=IdType.AUTO)
private Integer id;
/**
* 仓库编码
*/
private String locno;
/**
* 任务名称
*/
private String name;
/**
* 业务ID
*/
private String sid;
/**
* 类
*/
private String execBean;
/**
* 方法
*/
private String execMethod;
/**
* 参数
*/
private String execParams;
/**
* 执行结果
*/
private String execResult;
/**
* 执行签名MD5(类,方法,参数)
*/
private String execSign;
/**
* 执行时间
*/
private Integer execTime;
/**
* 执行次数
*/
private Integer tryCount;
/**
* 是否执行成功(0:否,1:是)
*/
private Integer successFlag;
/**
* 首次执行时间
*/
private Date firstTime;
/**
* 最后执行时间
*/
private Date lastTime;
/**
* 下次重试时间
*/
private Date nextTime;
/**
* 备注
*/
private String remarks;
/**
* 获取:ID
*/
public Integer getId() {
return id;
}
/**
* 设置:ID
*/
public void setId(Integer id) {
this.id = id;
}
/**
* 获取:仓库编码
*/
public String getLocno() {
return locno;
}
/**
* 设置:仓库编码
*/
public void setLocno(String locno) {
this.locno = locno;
}
/**
* 获取:任务名称
*/
public String getName() {
return name;
}
/**
* 设置:任务名称
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取:业务ID
*/
public String getSid() {
return sid;
}
/**
* 设置:业务ID
*/
public void setSid(String sid) {
this.sid = sid;
}
/**
* 获取:类
*/
public String getExecBean() {
return execBean;
}
/**
* 设置:类
*/
public void setExecBean(String execBean) {
this.execBean = execBean;
}
/**
* 获取:方法
*/
public String getExecMethod() {
return execMethod;
}
/**
* 设置:方法
*/
public void setExecMethod(String execMethod) {
this.execMethod = execMethod;
}
/**
* 获取:参数
*/
public String getExecParams() {
return execParams;
}
/**
* 设置:参数
*/
public void setExecParams(String execParams) {
this.execParams = execParams;
}
/**
* 获取:执行结果
*/
public String getExecResult() {
return execResult;
}
/**
* 设置:执行结果
*/
public void setExecResult(String execResult) {
this.execResult = execResult;
}
/**
* 获取:执行签名MD5(类,方法,参数)
*/
public String getExecSign() {
return execSign;
}
/**
* 设置:执行签名MD5(类,方法,参数)
*/
public void setExecSign(String execSign) {
this.execSign = execSign;
}
/**
* 获取:执行时间
*/
public Integer getExecTime() {
return execTime;
}
/**
* 设置:执行时间
*/
public void setExecTime(Integer execTime) {
this.execTime = execTime;
}
/**
* 获取:执行次数
*/
public Integer getTryCount() {
return tryCount;
}
/**
* 设置:执行次数
*/
public void setTryCount(Integer tryCount) {
this.tryCount = tryCount;
}
/**
* 获取:是否执行成功(0:否,1:是)
*/
public Integer getSuccessFlag() {
return successFlag;
}
/**
* 设置:是否执行成功(0:否,1:是)
*/
public void setSuccessFlag(Integer successFlag) {
this.successFlag = successFlag;
}
/**
* 获取:首次执行时间
*/
public Date getFirstTime() {
return firstTime;
}
/**
* 设置:首次执行时间
*/
public void setFirstTime(Date firstTime) {
this.firstTime = firstTime;
}
/**
* 获取:最后执行时间
*/
public Date getLastTime() {
return lastTime;
}
/**
* 设置:最后执行时间
*/
public void setLastTime(Date lastTime) {
this.lastTime = lastTime;
}
/**
* 获取:下次重试时间
*/
public Date getNextTime() {
return nextTime;
}
/**
* 设置:下次重试时间
*/
public void setNextTime(Date nextTime) {
this.nextTime = nextTime;
}
/**
* 获取:备注
*/
public String getRemarks() {
return remarks;
}
/**
* 设置:备注
*/
public void setRemarks(String remarks) {
this.remarks = remarks;
}
}
mapper:
package com.lx.task.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lx.task.entity.AsyncTaskEntity;
import org.apache.ibatis.annotations.Mapper;
/**
*
*类描述:异步任务表
*
*@Author:liuweiping
*@date:2021年04月11日
*@Version:1.0.0
*/
@Mapper
public interface AsyncTaskDao extends BaseMapper<AsyncTaskEntity> {
}
package com.lx.task.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lx.task.entity.AsyncTaskHistoryEntity;
import org.apache.ibatis.annotations.Mapper;
/**
*
*类描述:异步任务历史表
*
*@Author:liu wei ping
*@date:2019年08月12日
*@Version:1.0.0
*/
@Mapper
public interface AsyncTaskHistoryDao extends BaseMapper<AsyncTaskHistoryEntity> {
}
service:
package com.lx.task.service.impl;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.UUID;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import com.alibaba.fastjson.JSON;
import com.lx.annotation.AsyncTask;
import com.lx.conmon.UserInfo;
import com.lx.task.entity.AsyncTaskEntity;
import com.lx.task.mapper.AsyncTaskDao;
import com.lx.utils.IpUtils;
import com.lx.utils.Md5Utils;
/**
* 异步任务
* @author liu wei ping
*/
@Aspect
@Configuration
public class AsyncTaskAspect {
private static final Logger logger = LoggerFactory.getLogger(AsyncTaskAspect.class);
@Autowired
private AsyncTaskExecutor asyncTaskExecutor;
@Autowired
private AsyncTaskDao asyncTaskDao;
@Pointcut("@annotation(com.lx.annotation.AsyncTask)")
public void asyncTaskPointcut() {
}
@Around("asyncTaskPointcut()")
public Object asyncTaskRunning(ProceedingJoinPoint point) throws Throwable {
if (asyncTaskExecutor.isJobRunning()) {
return point.proceed();
}
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Method method = methodSignature.getMethod();
Class<?> targetClass = point.getTarget().getClass();
Method thisMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
AsyncTask asyncTask = thisMethod.getAnnotation(AsyncTask.class);
String taskName = asyncTask.value();
String sid = UUID.randomUUID().toString();
AsyncTaskEntity entity = new AsyncTaskEntity();
entity.setName(taskName);
entity.setExecBean(targetClass.getName());
entity.setExecMethod(method.getName());
entity.setExecParams(JSON.toJSONString(point.getArgs()));
entity.setExecSign(asyncTask.filterRepeatCall() ? Md5Utils.encryption(JSON.toJSONString(entity)) : sid);
entity.setSid(sid);
UserInfo dto = UserInfo.getUserInfo();
entity.setLocno(dto.getLocno());
entity.setCreateUserId(dto.getCreateUserId());
entity.setCreateUserName(dto.getCreateUserName());
entity.setUpdateUserId(dto.getUpdateUserId());
entity.setUpdateUserName(dto.getCreateUserName());
entity.setCreateTime(new Date());
entity.setNextTime(new Date(System.currentTimeMillis() + asyncTask.delay()));
entity.setTryCount(0);
entity.setSuccessFlag(0);
entity.setRemarks(IpUtils.getLocalIP());
asyncTaskDao.insert(entity);
return null;
}
}
package com.lx.task.service.impl;
import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.util.ParameterizedTypeImpl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lx.task.entity.AsyncTaskEntity;
import com.lx.task.entity.AsyncTaskHistoryEntity;
import com.lx.task.mapper.AsyncTaskDao;
import com.lx.task.mapper.AsyncTaskHistoryDao;
import com.lx.utils.IpUtils;
import com.lx.utils.RedisLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
/**
* 异步任务执行service
* @author liuweiping
*/
@Service
public class AsyncTaskExecutor {
private static final Logger logger = LoggerFactory.getLogger(AsyncTaskExecutor.class);
/**
* 定时任务调度线程池
*/
private ScheduledExecutorService mainExecutorService = new ScheduledThreadPoolExecutor(1);
/**
* 异步任务执行线程池
*/
private ExecutorService workExecutorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors() * 2, 10L,
TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(1024));
/**
* 启动后几秒开始检查
*/
private final static Integer INIT_DELAY_SEC = 30;
/**
* 每1分钟检查一次
*/
private final static Integer PERIOD_SEC = 60;
/***
* 最大锁定时间(30分钟) - 任务执行
*/
private static final int TIMEOUT_FOR_LOCK_JOB_SECONDS = 60 * 30;
/**
* 执行失败延迟执行时间
*/
private static final int TIME_OUT_FOR_NEXT = 1000 * 60 * 1;
/**
* 任务最大重试次数
*/
private static final int MAX_JOB_TRY_COUNT = 5;
/**
* 备注字段最大长度
*/
private int MAX_REMARKS_LEN = 1000;
/**
* 标记是否执行任务
*/
private ThreadLocal<Boolean> jobRunning = new ThreadLocal<>();
/**
* 标记是否转移异步任务数据
*/
private ThreadLocal<Boolean> storeJob = new ThreadLocal<>();
/**
* 标记是否转移异步任务数据
*/
private ThreadLocal<Integer> jobId = new ThreadLocal<>();
/**
* spring context
*/
private final ApplicationContext applicationContext;
@Value("${spring.application.name}")
private String appName;
@Resource
private AsyncTaskDao asyncTaskDao;
@Resource
private AsyncTaskHistoryDao asyncTaskHistoryDao;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
public AsyncTaskExecutor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@PostConstruct
public void init() {
mainExecutorService.scheduleAtFixedRate(() -> {selectJobs();},
INIT_DELAY_SEC, PERIOD_SEC, TimeUnit.SECONDS);
}
public boolean isJobRunning() {
return null != jobRunning.get() && jobRunning.get();
}
public Integer getJobId() {
return jobId.get();
}
private void selectJobs() {
try {
QueryWrapper<AsyncTaskEntity> wrapper = new QueryWrapper<AsyncTaskEntity>();
wrapper.lt("next_time", new Date());
wrapper.eq("success_flag", 0);
wrapper.le("try_count", MAX_JOB_TRY_COUNT);
wrapper.last(" limit 0,20");
List<AsyncTaskEntity> jobs = asyncTaskDao.selectList(wrapper);
if (jobs != null && jobs.size() > 0) {
jobs.forEach(item -> {
workExecutorService.submit(() -> {
doSingleJobWithLock(item);
});
});
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void doSingleJobWithLock(AsyncTaskEntity job) {
String lockKey = appName + ":async:lock:job:" + job.getId();
logger.info("doSingleJob , lock key {} ,id:{} ", lockKey, job.getId());
RedisLock lock = new RedisLock(stringRedisTemplate, lockKey);
if (lock.tryLock(TIMEOUT_FOR_LOCK_JOB_SECONDS)) {
try {
// 防止重复执行
QueryWrapper<AsyncTaskEntity> wrapper = new QueryWrapper<AsyncTaskEntity>();
wrapper.lt("next_time", new Date());
wrapper.eq("success_flag", 0);
wrapper.le("try_count", MAX_JOB_TRY_COUNT);
wrapper.eq("id", job.getId());
List<AsyncTaskEntity> asyncTaskEntityList = asyncTaskDao.selectList(wrapper);
if (CollectionUtils.isNotEmpty(asyncTaskEntityList)) {
execSingleJob(asyncTaskEntityList.get(0));
} else {
logger.info("doSingleJob already run id:{} ", job.getId());
}
} finally {
lock.unlock();
}
} else {
logger.info("doSingleJob , lock failed for key {} ,id:{} ", lockKey, job.getId());
}
}
private void execSingleJob(AsyncTaskEntity job) {
jobRunning.set(true);
storeJob.set(false);
jobId.set(job.getId());
logger.info("exec async job id {} , sid:{} , name {} , sign {} ", job.getId(), job.getSid(),
job.getName(), job.getExecSign());
Date startTime = new Date();
try {
if (job.getFirstTime() == null) {
job.setFirstTime(startTime);
}
job.setLastTime(startTime);
job.setTryCount(job.getTryCount() + 1);
JSONArray paramArray = JSON.parseArray(job.getExecParams());
Class<?> clazz = Class.forName(job.getExecBean());
Object bean = applicationContext.getBean(clazz);
Method method = null;
for (Method m : clazz.getMethods()) {
if (m.getName().equals(job.getExecMethod())
&& m.getParameterCount() == paramArray.size()) {
method = m;
break;
}
}
Class<?>[] parameterTypes = method.getParameterTypes();
int paramsLen = paramArray.size();
Object result = null;
if (null != parameterTypes && 0 != paramsLen) {
Object[] params = new Object[paramsLen];
Type[] types = method.getGenericParameterTypes();
for (int i = 0; i < paramsLen; i++) {
params[i] = caseParam(paramArray, parameterTypes[i], types[i], i);
}
result = method.invoke(bean, params);
} else {
result = method.invoke(bean);
}
job.setExecResult(JSON.toJSONString(result));
job.setRemarks(IpUtils.getLocalIP() + "执行成功");
job.setSuccessFlag(1);
job.setExecTime((int) (System.currentTimeMillis() - startTime.getTime()));
storeJob(job);
} catch (Exception e) {
logger.error("exec async job failed job-id-" + job.getId() + ", sid " + job.getSid()
+ " " + e.getMessage(), e);
job.setNextTime(new Date(System.currentTimeMillis() + TIME_OUT_FOR_NEXT * job.getTryCount()));
Throwable cause = e.getCause() != null ? e.getCause() : e;
job.setRemarks(IpUtils.getLocalIP() + "执行失败:" + cause.toString());
if (job.getRemarks().length() > MAX_REMARKS_LEN) {
job.setRemarks(job.getRemarks().substring(0, MAX_REMARKS_LEN));
}
asyncTaskDao.updateById(job);
} finally {
jobRunning.remove();
storeJob.remove();
jobId.remove();
}
}
public void storeJob(AsyncTaskEntity job) {
if (Boolean.FALSE.equals(storeJob.get())) {
if (null != job.getId()) {
asyncTaskDao.deleteById(job.getId());
}
AsyncTaskHistoryEntity history = new AsyncTaskHistoryEntity();
BeanUtils.copyProperties(job, history);
history.setId(null);
asyncTaskHistoryDao.insert(history);
storeJob.set(true);
}
}
private Object caseParam(JSONArray paramArray, Class<?> ctype, Type type, int index) {
if (ctype.isAssignableFrom(List.class) && type instanceof ParameterizedTypeImpl) {
ParameterizedTypeImpl typeImpl = (ParameterizedTypeImpl) type;
return JSON.parseArray(JSON.toJSONString(paramArray.get(index)),
(Class) typeImpl.getActualTypeArguments()[0]);
} else {
return paramArray.getObject(index, ctype);
}
}
}
package com.lx.task.service.impl;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import com.alibaba.fastjson.JSON;
import com.lx.task.entity.AsyncTaskEntity;
import com.lx.task.mapper.AsyncTaskDao;
import com.lx.utils.IpUtils;
/**
* 异步任务
* @author liu wei ping
*/
@Aspect
@Configuration
public class TransactionalAspect {
private static final Logger logger = LoggerFactory.getLogger(TransactionalAspect.class);
@Autowired
private AsyncTaskExecutor asyncTaskExecutor;
@Autowired
private AsyncTaskDao asyncTaskDao;
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalPointcut() {
}
@Around("transactionalPointcut()")
public Object transactionalRunning(ProceedingJoinPoint point) throws Throwable {
if (asyncTaskExecutor.isJobRunning()) {
Date startTime = new Date();
Object result = point.proceed();
AsyncTaskEntity job = asyncTaskDao.selectById(asyncTaskExecutor.getJobId());
job.setExecResult(JSON.toJSONString(result));
job.setRemarks(IpUtils.getLocalIP() + " 执行成功");
job.setSuccessFlag(1);
job.setExecTime((int) (System.currentTimeMillis() - startTime.getTime()));
asyncTaskExecutor.storeJob(job);
return result;
} else {
return point.proceed();
}
}
}
sql脚本:
CREATE TABLE `t_async_task` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`locno` varchar(10) NOT NULL DEFAULT '' COMMENT '仓库编码',
`name` varchar(500) NOT NULL DEFAULT '' COMMENT '任务名称',
`sid` varchar(50) NOT NULL DEFAULT '' COMMENT '业务ID',
`exec_bean` varchar(500) NOT NULL DEFAULT '' COMMENT '类',
`exec_method` varchar(500) NOT NULL DEFAULT '' COMMENT '方法',
`exec_params` longtext COMMENT '参数',
`exec_result` varchar(3000) DEFAULT '' COMMENT '执行结果',
`exec_sign` varchar(50) NOT NULL DEFAULT '' COMMENT '执行签名MD5(类,方法,参数)',
`exec_time` int(11) NOT NULL DEFAULT '0' COMMENT '执行时间',
`try_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行次数',
`success_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否执行成功(0:否,1:是)',
`first_time` datetime DEFAULT NULL COMMENT '首次执行时间',
`last_time` datetime DEFAULT NULL COMMENT '最后执行时间',
`next_time` datetime NOT NULL COMMENT '下次重试时间',
`remarks` varchar(3000) NOT NULL DEFAULT '' COMMENT '备注',
`trace_id` varchar(128) DEFAULT NULL COMMENT '日志跟踪id',
`create_user_id` varchar(50) DEFAULT NULL COMMENT '创建人ID',
`create_user_name` varchar(50) DEFAULT NULL COMMENT '创建人名称',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_user_id` varchar(50) DEFAULT NULL COMMENT '更新人ID',
`update_user_name` varchar(50) DEFAULT NULL COMMENT '更新人名称',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标识(0-正常,1-删除)',
PRIMARY KEY (`id`),
KEY `next_time` (`next_time`),
KEY `sid` (`sid`),
KEY `success_flag` (`success_flag`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT='异步任务表';
CREATE TABLE `t_async_task_history` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`locno` varchar(10) NOT NULL DEFAULT '' COMMENT '仓库编码',
`name` varchar(500) NOT NULL DEFAULT '' COMMENT '任务名称',
`sid` varchar(50) NOT NULL DEFAULT '' COMMENT '业务ID',
`exec_bean` varchar(500) NOT NULL DEFAULT '' COMMENT '类',
`exec_method` varchar(500) NOT NULL DEFAULT '' COMMENT '方法',
`exec_params` longtext COMMENT '参数',
`exec_result` varchar(3000) DEFAULT '' COMMENT '执行结果',
`exec_sign` varchar(50) NOT NULL DEFAULT '' COMMENT '执行签名MD5(类,方法,参数)',
`exec_time` int(11) NOT NULL DEFAULT '0' COMMENT '执行时间',
`try_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行次数',
`success_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '是否执行成功(0:否,1:是)',
`first_time` datetime DEFAULT NULL COMMENT '首次执行时间',
`last_time` datetime DEFAULT NULL COMMENT '最后执行时间',
`next_time` datetime NOT NULL COMMENT '下次重试时间',
`remarks` varchar(3000) NOT NULL DEFAULT '' COMMENT '备注',
`trace_id` varchar(128) DEFAULT NULL COMMENT '日志跟踪id',
`create_user_id` varchar(50) DEFAULT NULL COMMENT '创建人ID',
`create_user_name` varchar(50) DEFAULT NULL COMMENT '创建人名称',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_user_id` varchar(50) DEFAULT NULL COMMENT '更新人ID',
`update_user_name` varchar(50) DEFAULT NULL COMMENT '更新人名称',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` tinyint(4) NOT NULL DEFAULT '0' COMMENT '删除标识(0-正常,1-删除)',
PRIMARY KEY (`id`),
KEY `next_time` (`next_time`),
KEY `sid` (`sid`),
KEY `success_flag` (`success_flag`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='异步任务表';
测试如下:
这样就实现了分布式事务失败补偿机制,当重试6次失败,则需要人为去处理失败异常!