1. 程式人生 > 其它 >一種執行非同步任務的設計

一種執行非同步任務的設計

先上程式碼,使用了 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.執行緒池可以使用公用的