一種執行非同步任務的設計
阿新 • • 發佈:2022-05-24
先上程式碼,使用了 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.執行緒池可以使用公用的