一种执行异步任务的设计
先上代码,使用了 lombok 库简化了一些样板代码。
@Accessors(chain = true)
@Data
@RequiredArgsConstructor(staticName = "of")
public class BaseRspDTO<T extends Object> {
// 区分是 DTO 返回的唯一标记,比如是 UserInfoDTO 还是 BannerDTO
@NonNull private String key;
// 返回的 data
private T data;
}
/**
* 执行异步任务
*/
public class Task {
public static List<BaseRspDTO<Object>> execute(List<Callable<BaseRspDTO<Object>>> taskList, long timeOut, ExecutorService executor) {
List<BaseRspDTO<Object>> resultList = new ArrayList<>();
// 校验参数
if (taskList == null || taskList.size() == 0 || executor == null || timeOut <= 0) {
return resultList;
}
// 提交任务
CompletionService<BaseRspDTO<Object>> baseDTOCompletionService = new ExecutorCompletionService<BaseRspDTO<Object>>(executor);
for (Callable<BaseRspDTO<Object>> task : taskList) {
baseDTOCompletionService.submit(task);
}
try {
// 遍历获取结果
for (int i = 0; i < taskList.size(); i++) {
Future<BaseRspDTO<Object>> baseRspDTOFuture = baseDTOCompletionService.poll(timeOut, TimeUnit.SECONDS);
resultList.add(baseRspDTOFuture.get());
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return resultList;
}
}
private void featureMethod(HttpServletRequest request, UserQO qo, User user) {
//统计 UA
Callable<BaseRspDTO<Object>> uaTask = () -> {
healthService.addUserIdToRedisOnlineUserCountStatistics(qo.getHead().getCurrentUserId());
return BaseRspDTO.of("ua");
};
// 如果token有效,刷新token
Callable<BaseRspDTO<Object>> authCodeTask = () -> {
String authCode = request.getParameter("authCode");
if (authCode != null) {
userService.tokenReFlush(authCode);
}
return BaseRspDTO.of("authCode").setData(authCode);
};
ExecutorService executor = Executors.newFixedThreadPool(10);
List<BaseRspDTO<Object>> resultList = Task.execute(List.of(uaTask, authCodeTask), 3, executor);
if (resultList.size() == 0) {
return;
}
resultList.forEach(result -> {
if (StringUtils.equals("authCode", result.getKey()) && null != result.getData()) {
user.setAuthCode((String) result.getData());
}
});
}
设计思路
BaseRspDTO 用于承载不同异步任务的返回值,使用 key 作为标识,方便类型强转
Task 类是一个工具类,用于执行多个异步任务
Callable<BaseRspDTO<Object>> uaTask = () -> {
healthService.addUserIdToRedisOnlineUserCountStatistics(qo.getHead().getCurrentUserId());
return BaseRspDTO.of("ua");
};
先将原有串行方法进行改造,将其放入一个 Callable 中,并返回 BaseRspDTO,并定义 key。
ExecutorService executor = Executors.newFixedThreadPool(10);
List<BaseRspDTO<Object>> resultList = Task.execute(List.of(uaTask, authCodeTask), 3, executor);
定义线程池,使用 Task.execute 进行异步任务的编排和调用
resultList.forEach(result -> {
if (StringUtils.equals("authCode", result.getKey()) && null != result.getData()) {
user.setAuthCode((String) result.getData());
}
});
最后循环所有的结果,根据 key 来获取返回值。
后续可供优化点
1.key 可以使用枚举
2.样板代码还是很多
3.可以考虑使用策略模式对结果进行解耦,消除多个 if 语句
4.线程池可以使用公用的