项目源码:使用AOP完成长任务轮询
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
需求背景
有一个接口实现合并文件,当文件过多时,预计要三分钟或者更长时间,这个时候前端调用的时候,由于后端长时间未返回结果,就会出现接口的超时的问题,于是就需要将任务异步执行,前端轮询查询任务的执行情况。考虑项目中接口代码已经完成,这个时候再修改比较麻烦,所以使用自定义注解,使用Spring AOP切点,异步执行任务,最后把任务结果缓存
流程图
代码实现
自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisTaskStatus {
}
切点
@Aspect
@Component
public class RedisTaskStatusAspect {
@Autowired
private RedisUtil redisUtil;
@Pointcut("@annotation(org.jeecg.common.aspect.annotation.RedisTaskStatus)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point){
String uuid = UUID.randomUUID().toString().replace("-", "");
new Thread(() -> {
Object result = null;
try {
result = point.proceed();
} catch (Throwable throwable) {
result = Result.error("执行任务异常"+throwable.getMessage());
} finally {
//结果缓存
redisUtil.set(uuid, result, 600);
}
}, uuid).start();
return Result.ok(uuid);
}
}
- 这里我们返回了一个UUID给前端,前端定时轮询使用这个UUID调用查询结果的接口
- 将执行的结果存入到redis,这里由于前端会定时轮询接口,所以将结果存入缓存,前端调用的时候查询缓存,而不是查询数据库
方法调用
@PostMapping(value = "/submit")
@RedisTaskStatus
public Result<?> submit(@RequestBody SubmitDataDTO submitDataDTO) {
try {
flowService.submit(submitDataDTO);
} catch (Exception e) {
return Result.error(e.getMessage());
}
return Result.ok();
}
轮询接口
@GetMapping(value = "/pollresult")
public Result<?> pollResultRedisTaskStatus(@RequestParam(name="id") String id) {
if(StringUtils.isEmpty(id)){
Result.error("传入查询的值为空");
}
Result result= (Result) redisUtil.get(id);
if(result!=null){
log.info("轮询结果result:{}",result);
return result;
}
//自定义code 6056 标记任务未完成
return Result.error(6056, "任务还没完成");
}