自动补偿实现
- 要求: 方法调用的过程中。失败的时候,系统有办法进行自动重试,重试达到一定次数后,钉钉通知开发
- 实现设计: 注解,反射,定时任务
表的设计
– 补偿表
CREATE TABLE `a_compensation` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '数据库主键',
`busKey` VARCHAR(255) NOT NULL COMMENT '业务主键',
`type` VARCHAR(20) NOT NULL DEFAULT 'auto' COMMENT '处理方式:自动(auto),manual(人工介入)',
`dataStatus` TINYTEXT NOT NULL COMMENT '数据状态:0待处理,1:成功,2:失败',
`className` VARCHAR(200) NOT NULL COMMENT '完整类名',
`beanName` VARCHAR(100) NOT NULL COMMENT '类名',
`methodName` VARCHAR(100) NOT NULL COMMENT '方法名',
`reqArgsType` VARCHAR(500) NOT NULL COMMENT '方法入参类型',
`reqArgs` TEXT NOT NULL COMMENT '方法入参参数',
`reqArgsTypeReal` VARCHAR(500) DEFAULT NULL COMMENT '实际参数类型',
`retryCount` BIGINT(10) NOT NULL COMMENT '重试次数',
`resultMsg` VARCHAR(2000) DEFAULT NULL COMMENT '返回信息',
`gmtCreate` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
`gmtModified` TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '更新时间',
`isValid` TINYINT(4) NOT NULL DEFAULT '1' COMMENT '是否有效',
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
代码
- 注解
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Compensate {
/**
* 异常类型,用于补偿指定异常的数据,默认值Exception
*/
Class<?>[] exceptionType() default Exception.class;
}
- 切面类
@Aspect
@Component
public class AnnotationCompensateEntry {
private static final Logger logger = LoggerFactory.getLogger(AnnotationCompensateEntry.class);
@Autowired
private CompensationService compensationService;
/**
* 切点,通过注解的方式
*
* @param joinPoint 连接点
* @param e 异常
*/
@AfterThrowing(pointcut = "@annotation(com.base.annotation.Compensate)", throwing = "e")
public void aopCompensate(JoinPoint joinPoint, Exception e) {
try {
// 1:获取需要的数据,并且进行验证
Method targetMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
Compensate compensate = targetMethod.getAnnotation(Compensate.class);
Object[] args = joinPoint.getArgs();
if (args.length == 0) {
logger.info("无参方法无法补偿!");
return;
}
//异常类型
Class<?>[] exceptionTypes = compensate.exceptionType();
for (Class<?> exceptionType : exceptionTypes) {
// exceptionType 是 e.getClass() 的父类或者一样的。
if (exceptionType.isAssignableFrom(e.getClass())) {
saveCompensationDO(e, joinPoint);
}
}
} catch (Throwable throwable) {
// 异常需要抛出。要不然没办法,业务代码依赖这个报错的信息会被干扰。
logger.error(throwable.getMessage(), throwable);
Throwables.propagateIfPossible(throwable);
}
}
/**
* 2: 保存异常数据
* 保存的数据,应该先查询下,看数据库中是否存在这样的数据,如果不存在就进行插入。存在啥也不做。
*
* @param e 异常类
* @param joinPoint 切面类
*/
public void saveCompensationDO(Exception e, JoinPoint joinPoint) throws JsonProcessingException {
// 1:获取切面对象的参数
MethodInfo methodInfo = new MethodInfo(joinPoint);
methodInfo.init();
// 2:构建需要补偿的对象
CompensationEntity compensationDO = buildCompensationDO(methodInfo, e);
// 3: 上面根据的数据,得保证是满足数据的完整性。然后进行数据的储存入库。然后结束。
List<CompensationEntity> compensationEntities = compensationService.findByProperty(CompensationEntity.class, "busKey", compensationDO.getBusKey());
if (CollectionUtils.isEmpty(compensationEntities)) {
compensationService.save(compensationDO);
}
}
/**
* 2-1: 数据转换
*
* @param methodInfo 包装过后的数据对象
* @return 转换的补偿类
*/
private CompensationEntity buildCompensationDO(MethodInfo methodInfo, Exception e) {
// 1:存入通用的参数
CompensationEntity compensationDO = new CompensationEntity();
Annotation[] annotations= methodInfo.getTargetClass().getAnnotations();
String springBeanName="";
for (Annotation annotation : annotations) {
if (annotation.annotationType().equals(Service.class)){
springBeanName=methodInfo.