如何解決異步任務上下文丟失問題
解決異步任務上下文丟失問題
- 上下文丟失主要是因為主線程和子線程的上下文不能共享。
- 可以通過執(zhí)行異步任務之前,將主線程上的上下文信息拷貝到子線程上。
自定義TaskDecorator 來拷貝主線程上的上下文信息到子線程,然后將自定義的 TaskDecorator實現(xiàn)類 設置到線程池上。
@Configuration
public class ThreadPoolConfig {
@Bean(name = "customizeTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
// 核心線程數(shù)=cpu核心數(shù)+1
poolExecutor.setCorePoolSize(5);
// 最大線程數(shù)=cpu核心數(shù)*2
poolExecutor.setMaxPoolSize(8);
// 設置任務裝飾器
poolExecutor.setTaskDecorator(taskDecorator());
// 任務被拒絕后,交給調(diào)用線程執(zhí)行
poolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return poolExecutor;
}
@Bean
public TaskDecorator taskDecorator(){
return new TaskDecorator() {
@Override
public Runnable decorate(Runnable runnable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return ()->{
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
runnable.run();
}finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
};
}
}
Fegin異步情況丟失上下文問題
在微服務的開發(fā)中,我們經(jīng)常需要服務之間的調(diào)用,并且為了提高效率使用異步的方式進行服務之間的調(diào)用,在這種異步的調(diào)用情況下會有一個嚴重的問題,丟失上文下

通過以上圖片可以看出異步丟失上下文的原因是不在同一個線程,所有數(shù)據(jù)不能共享,Wie了解決這個問題,我們就需要把之前線程的請求頭上下文,在次存放到其他線程的請求頭上下文就行,具體實現(xiàn)如下:

案例:feign異步獲取訂單明細的案例代碼
/**
* 獲取訂單明細的vo
* @return
*/
@Override
public OrderConfirmVo orderConfirm() {
MemberResponseVo member = OrderInterceptor.threadLocal.get();
OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
System.out.println("主線程:"+ Thread.currentThread().getId());
//獲取主線程的請求頭信息
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//考慮到效率問題 使用異步編排
CompletableFuture<Void> getAddress = CompletableFuture.runAsync(() -> {
//子線程中設置添加主線程的請求頭信息 信息共享 否則遠程調(diào)用異步處理丟失請求頭信息
RequestContextHolder.setRequestAttributes(requestAttributes);
System.out.println("address:"+ Thread.currentThread().getId());
//遠程獲取地址信息
List<MemberAddressVo> address = memberFeignService.getAddress(member.getId());
orderConfirmVo.setAddress(address);
}, executor);
CompletableFuture<Void> getItem = CompletableFuture.runAsync(() -> {
//子線程中設置添加主線程的請求頭信息 信息共享 否則遠程調(diào)用異步處理丟失請求頭信息
RequestContextHolder.setRequestAttributes(requestAttributes);
System.out.println("item:"+ Thread.currentThread().getId());
//遠程獲取購物項
List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
orderConfirmVo.setItems(currentUserCartItems);
}, executor).thenRunAsync(()->{
List<OrderItemVo> items = orderConfirmVo.getItems();
//獲取所有商品的id
List<String> collect = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
List<Long> skuIds = collect.stream().map(item -> {
return Long.parseLong(item);
}).collect(Collectors.toList());
R<List<SkuHasStockVo>> skusHasStock = wmsFeignService.getSkusHasStock(skuIds);
List<SkuHasStockVo> data = skusHasStock.getData(new TypeReference<List<SkuHasStockVo>>() {
});
if(data!= null){
Map<Long, Boolean> collect1 = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
orderConfirmVo.setStocks(collect1);
}
},executor);
//異步編排完成之后執(zhí)行后續(xù)操作
try {
CompletableFuture.allOf(getAddress,getItem).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
orderConfirmVo.setIntegration(member.getIntegration());
orderConfirmVo.setPayPrice(orderConfirmVo.getPayPrice());
orderConfirmVo.setTotal(orderConfirmVo.getTotal());
//TODO 放重處理 生成token令牌儲存在redis
String token = UUID.randomUUID().toString().replace("-", "");
orderConfirmVo.setOrderToken(token);
redisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN+member.getId(),token);
return orderConfirmVo;
}總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
IDEA巧用Postfix Completion讓碼速起飛(小技巧)
這篇文章主要介紹了IDEA巧用Postfix Completion讓碼速起飛,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-08-08
Java將文件夾保留目錄打包為 ZIP 壓縮包并下載的教程詳解
這篇文章主要介紹了Java將文件夾保留目錄打包為 ZIP 壓縮包并下載的教程詳解,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08
Java中通過三級緩存解決Spring循環(huán)依賴詳解
這篇文章主要介紹了Java中通過三級緩存解決Spring循環(huán)依賴詳解,當出現(xiàn)兩個或多個 Bean 在初始化時相互依賴的情況時,Spring Boot 會將其中一個 Bean 提前暴露出來,以便其他 Bean 能夠在初始化時正確地引用它,這一策略能有效避免循環(huán)依賴導致的問題,需要的朋友可以參考下2023-09-09

