CompletableFuture 和 @Async 相互切换运用,异步任务切面处理token,非void类型异常处理

CompletableFuture 异常处理

private void asyncChildFaults(FaultTreeRequest request, List<FmeaTreeNodeModel> fmeaTreeNodeModels) {
    List<CompletableFuture<Void>> futures = new ArrayList<>();
    String token = AccountContext.currentToken();
    for (FmeaTreeNodeModel node : fmeaTreeNodeModels) {
        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            try {
                AccountContext.saveToken(token);
                setChildFault(request, node);
            } catch (Exception exception) {
                throw new CompletionException(exception);
            } finally {
                AccountContext.removeToken();
            }
        });
        futures.add(future);
    }
    try {
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    } catch (CompletionException exception) {
        Throwable errorCause = exception.getCause();
        throw new AppException(getCode(errorCause), errorCause.getMessage());
    } catch (Exception exception) {
        log.info("FmeaService::asyncChildFaults::{}", exception.getMessage());
    }
}

private Integer getCode(Throwable errorCause) {
    if (errorCause instanceof AppException) {
        return ((AppException) errorCause).getCode();
    }
    return HttpStatus.BAD_REQUEST.value();
}
 

全局异常处理配置


    @ResponseBody
    @ExceptionHandler(value = ExecutionException.class)
    public ResponseEntity<Result<Void>> executionExceptionHandler(ExecutionException exception) {
        LogUtil.error("ExecutionException exception:", exception);
        ResponseEntity<Result<Void>> response = getConcurrentExceptionResponse(exception);
        if (response != null) {
            return response;
        }
        return ResponseEntity.status(HttpStatus.OK).body(Result.fail(ResultCodeEnum.ASYNCHRONOUS_TASK_EXCEPTION));
    }

    @ResponseBody
    @ExceptionHandler(value = CompletionException.class)
    public ResponseEntity<Result<Void>> completionExceptionHandler(CompletionException exception) {
        LogUtil.error("CompletionException exception:", exception);
        ResponseEntity<Result<Void>> response = getConcurrentExceptionResponse(exception);
        if (response != null) {
            return response;
        }
        return ResponseEntity.status(HttpStatus.OK).body(Result.fail(ResultCodeEnum.ASYNCHRONOUS_TASK_EXCEPTION));
    }

    // AppException 自定义异常 需要处理的异常信息
    @Nullable
    private ResponseEntity<Result<Void>> getConcurrentExceptionResponse(Exception exception) {
        Throwable errorCause = Optional.of(exception).map(Exception::getCause).orElseGet(Exception::new);
        if (errorCause instanceof AppException) {
            return ResponseEntity.status(HttpStatus.OK)
                .body(Result.fail(((AppException) errorCause).getCode(), errorCause.getMessage()));
        }
        return null;
    }

全局异常处理 上面业务代码改造

private void asyncChildFaults(FaultTreeRequest request, List<FmeaTreeNodeModel> fmeaTreeNodeModels) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        String token = AccountContext.currentToken();
        fmeaTreeNodeModels.forEach(node -> {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                AccountContext.saveToken(token);
                setChildFault(request, node);
            });
            futures.add(future);
        });
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    }

切面处理token

注解

@Target( {ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AsyncToken {
    String value() default "";
}

切面处理


@Aspect
@Component
public class AsyncTokenAspect {
    // 配自己的注解路径
    @Pointcut("@annotation(com.*.*.requirement.annotation.AsyncToken)")
    public void asyncMethod() {
    }

    @Around("asyncMethod()")
    public Object setToken(ProceedingJoinPoint joinPoint) throws Throwable {
        Object[] args = joinPoint.getArgs();
        // token 位于第一个参数
        Object firstParm = Stream.of(args).findFirst().orElseGet(Object::new);
        String token = "";
        if (firstParm instanceof String) {
            token = (String) firstParm;
        }
        AccountContext.saveToken(token);
        try {
            // 执行原始方法
            return joinPoint.proceed();
        } finally {
            // 清除token值
            AccountContext.removeToken();
        }
    }
}

Async 异步方法

    /**
     * 获取设计模式列表
     *
     * @param token token
     * @param flatList 设计模式列表 扁平化结构
     * @return CompletableFuture<Void>
     */
    @Async
    @AsyncToken
    public CompletableFuture<Void> getAllReliabilityModels(String token, List<ReliabilitySpecificationTreeModel> flatList) {
        String docId = reliabilitySpecificationProperties.getDocId();
        String modelItemId = reliabilitySpecificationProperties.getModelItemId();
        // 这里调用了Feign接口需要token 认证
        commonBusiness.getReliabilitySpecificationTreeModels(docId, modelItemId, RELIABILITY_MODEL, flatList);
        return CompletableFuture.completedFuture(null);
    }

    @Override
    public List<ReliabilitySpecificationTreeModel> getAllModelsAndFuncPoints() {
        List<ReliabilitySpecificationTreeModel> allReliabilityModels = new ArrayList<>();
        List<ReliabilitySpecificationTreeModel> allReliabilityFuncPoints = new ArrayList<>();
        String token = AccountContext.currentToken();
        CompletableFuture<Void> reliabilityModelsFuture = dfxAnalysisRelationAsyncService.getAllReliabilityModels(token,
            allReliabilityModels);
        CompletableFuture<Void> reliabilityFuncPointsFuture
            = dfxAnalysisRelationAsyncService.getAllReliabilityFuncPoints(token, allReliabilityFuncPoints);
        CompletableFuture.allOf(reliabilityModelsFuture, reliabilityFuncPointsFuture).join();
        return Stream.of(allReliabilityModels, allReliabilityFuncPoints)
            .flatMap(List::stream)
            .collect(Collectors.toList());
    }

CompletableFuture 转成 @Async

原来

xxxService类中

没有利用全局异常捕获

private void asyncChildFaults(FaultTreeRequest request, List<FmeaTreeNodeModel> fmeaTreeNodeModels) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        String token = AccountContext.currentToken();
        fmeaTreeNodeModels.forEach(node -> {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                AccountContext.saveToken(token);
                setChildFault(request, node);
            });
            try {
                // 获取异常信息
                future.get();
            } catch (ExecutionException exception) {
                Throwable errorCause = Optional.of(exception).map(Exception::getCause).orElseGet(Exception::new);
                throw new AppException(getCode(errorCause), errorCause.getMessage());
            } catch (Exception exception) {
                log.info("FmeaService::asyncChildFaults::{}", exception.getMessage());
            } finally {
                AccountContext.removeToken();
            }
            futures.add(future);
        });
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    }

改成全局异常捕获 代码更加简洁了

但是异步任务代码和普通业务代码糅合在一块了,代码篇幅比较繁杂

private void asyncChildFaults(FaultTreeRequest request, List<FmeaTreeNodeModel> fmeaTreeNodeModels) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        String token = AccountContext.currentToken();
        fmeaTreeNodeModels.forEach(node -> {
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                AccountContext.saveToken(token);
                setChildFault(request, node);
            });
            futures.add(future);
        });
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    }
private  void setChildFault(String token, FaultTreeRequest request, FmeaTreeNodeModel node) {
        request.setId(node.getId());
        request.setType(node.getNodeType());
        request.setName(node.getName());
        FmeaObjectAggregateModel objectModel = Optional.ofNullable(integrationFeign.failureModeList(request))
            .map(com.huawei.mideas.requirement.model.Result::getData)
            .orElseGet(FmeaObjectAggregateModel::new);
        if (StringUtils.isBlank(objectModel.getId())) {
            return;
        }
        // set 分析对象属性
        setObjectField(node, objectModel);
        BeanUtils.copyProperties(objectModel, node);
        if (CollectionUtils.isEmpty(objectModel.getChildren())) {
            return;
        }
        List<FmeaTreeNodeModel> childNode = Utils.objectToList(objectModel.getChildren(), FmeaTreeNodeModel.class);
        node.setChildren(childNode);
    }

    private void setObjectField(FmeaTreeNodeModel node, FmeaObjectAggregateModel objectModel) {
        node.setAnalysisObjectId(objectModel.getId());
        node.setCode(objectModel.getCode());
        node.setVersion(objectModel.getVersion());
        node.setCategory(objectModel.getCategory());
        node.setStatus(objectModel.getStatus());
    }

CompletableFuture 转成 @Async

新加一个 xxxAsyncService类,将异步代码逻辑写在这个类中

@Service
public class xxxAsyncService {
    @Resource
    private IntegrationFeign integrationFeign;

    /**
     * 封装失效模式数据
     *
     * @param token token
     * @param request 入参请求体
     * @param node 失效模式树节点
     */
    @Async
    @AsyncToken
    public CompletableFuture<Void> setChildFault(String token, FaultTreeRequest request, FmeaTreeNodeModel node) {
        request.setId(node.getId());
        request.setType(node.getNodeType());
        request.setName(node.getName());
        FmeaObjectAggregateModel objectModel = Optional.ofNullable(integrationFeign.failureModeList(request))
            .map(com.huawei.mideas.requirement.model.Result::getData)
            .orElseGet(FmeaObjectAggregateModel::new);
        if (StringUtils.isBlank(objectModel.getId())) {
            return CompletableFuture.completedFuture(null);
        }
        // set 分析对象属性
        setObjectField(node, objectModel);
        BeanUtils.copyProperties(objectModel, node);
        if (CollectionUtils.isEmpty(objectModel.getChildren())) {
            return CompletableFuture.completedFuture(null);
        }
        List<FmeaTreeNodeModel> childNode = Utils.objectToList(objectModel.getChildren(), FmeaTreeNodeModel.class);
        node.setChildren(childNode);
        return CompletableFuture.completedFuture(null);
    }

    private void setObjectField(FmeaTreeNodeModel node, FmeaObjectAggregateModel objectModel) {
        node.setAnalysisObjectId(objectModel.getId());
        node.setCode(objectModel.getCode());
        node.setVersion(objectModel.getVersion());
        node.setCategory(objectModel.getCategory());
        node.setStatus(objectModel.getStatus());
    }
}

xxxService调用 异步方法

    @Resource
    private FmeaAsyncService fmeaAsyncService;

    private void asyncChildFaults(FaultTreeRequest request, List<FmeaTreeNodeModel> fmeaTreeNodeModels) {
        List<CompletableFuture<Void>> futures = new ArrayList<>();
        String token = AccountContext.currentToken();
        fmeaTreeNodeModels.forEach(node -> {
            CompletableFuture<Void> future = fmeaAsyncService.setChildFault(token, request, node);
            futures.add(future);
        });
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    }

赋值token优化

主线程token传递到子线程
1 方法局部变量
2 InheritableThreadLocal
3 线程池
4 全局变量
...
上面是第一种方法,方法局部变量,但是代码有点冗余
下面优化成第二种方法

public class ThreadLocalUtil {
    // 可继承主线程
    private static final ThreadLocal<String> INHERITABLE_TOKEN = new InheritableThreadLocal<>();

    public static void setToken(String token) {
        INHERITABLE_TOKEN.set(token);
    }

    public static String getToken() {
        return INHERITABLE_TOKEN.get();
    }

    public static void removeToken() {
        INHERITABLE_TOKEN.remove();
    }
}

@Aspect
@Component
public class AsyncTokenAspect {
    @Pointcut("@annotation(com.huawei.mideas.requirement.annotation.AsyncToken)")
    public void asyncToken() {
    }

    @Around("asyncToken()")
    public Object setToken(ProceedingJoinPoint joinPoint) throws Throwable {
        AccountContext.saveToken(ThreadLocalUtil.getToken());
        try {
            // 执行原始方法
            return joinPoint.proceed();
        } finally {
            // 清除token值
            ThreadLocalUtil.removeToken();
            AccountContext.removeToken();
        }
    }
}
    @Async
    @AsyncToken
    public CompletableFuture<Void> getAllReliabilityModels(List<ReliabilitySpecificationTreeModel> flatList) {
        String docId = reliabilitySpecificationProperties.getDocId();
        String modelItemId = reliabilitySpecificationProperties.getModelItemId();
        commonBusiness.getReliabilitySpecificationTreeModels(docId, modelItemId, RELIABILITY_MODEL, flatList);
        return CompletableFuture.completedFuture(null);
    }
    public List<ReliabilitySpecificationTreeModel> getAllModelsAndFuncPoints() {
        List<ReliabilitySpecificationTreeModel> allReliabilityModels = new ArrayList<>();
        List<ReliabilitySpecificationTreeModel> allReliabilityFuncPoints = new ArrayList<>();
        ThreadLocalUtil.setToken(AccountContext.currentToken());
        CompletableFuture<Void> reliabilityModelsFuture = dfxAnalysisRelationAsyncService.getAllReliabilityModels(allReliabilityModels);
        CompletableFuture<Void> reliabilityFuncPointsFuture
            = dfxAnalysisRelationAsyncService.getAllReliabilityFuncPoints(allReliabilityFuncPoints);
        CompletableFuture.allOf(reliabilityModelsFuture, reliabilityFuncPointsFuture).join();
        return Stream.of(allReliabilityModels, allReliabilityFuncPoints)
            .flatMap(List::stream)
            .collect(Collectors.toList());
    }

InheritableThreadLocal 问题

造成无法拿到父线程数据的原因可能有以下几种情况:

  1. 父线程没有设置值:如果在父线程中没有设置 InheritableThreadLocal 的值,子线程就无法继承任何值。
  2. 父线程在子线程创建之后修改了值:InheritableThreadLocal 的值是在子线程创建时继承的,如果父线程在子线程创建之后修改了值,子线程将无法获取到最新的值。
  3. 子线程在父线程设置值之前就开始执行:如果子线程在父线程设置 InheritableThreadLocal 的值之前就开始执行,那么子线程将无法获取到父线程的值。
  4. 使用了线程池:如果使用线程池来管理线程,线程池可能会重用线程。在这种情况下,子线程可能会继承之前线程的值,而不是父线程的值。
    @Async 使用了线程池,容易造成token赋值不了还是null
posted @ 2023-08-16 14:47  linzm14  阅读(130)  评论(0编辑  收藏  举报