如何解決異步任務(wù)上下文丟失問題
解決異步任務(wù)上下文丟失問題
- 上下文丟失主要是因?yàn)橹骶€程和子線程的上下文不能共享。
- 可以通過執(zhí)行異步任務(wù)之前,將主線程上的上下文信息拷貝到子線程上。
自定義TaskDecorator 來(lái)拷貝主線程上的上下文信息到子線程,然后將自定義的 TaskDecorator實(shí)現(xiàn)類 設(shè)置到線程池上。
@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);
// 設(shè)置任務(wù)裝飾器
poolExecutor.setTaskDecorator(taskDecorator());
// 任務(wù)被拒絕后,交給調(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異步情況丟失上下文問題
在微服務(wù)的開發(fā)中,我們經(jīng)常需要服務(wù)之間的調(diào)用,并且為了提高效率使用異步的方式進(jìn)行服務(wù)之間的調(diào)用,在這種異步的調(diào)用情況下會(huì)有一個(gè)嚴(yán)重的問題,丟失上文下

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

案例:feign異步獲取訂單明細(xì)的案例代碼
/**
* 獲取訂單明細(xì)的vo
* @return
*/
@Override
public OrderConfirmVo orderConfirm() {
MemberResponseVo member = OrderInterceptor.threadLocal.get();
OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
System.out.println("主線程:"+ Thread.currentThread().getId());
//獲取主線程的請(qǐng)求頭信息
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//考慮到效率問題 使用異步編排
CompletableFuture<Void> getAddress = CompletableFuture.runAsync(() -> {
//子線程中設(shè)置添加主線程的請(qǐng)求頭信息 信息共享 否則遠(yuǎn)程調(diào)用異步處理丟失請(qǐng)求頭信息
RequestContextHolder.setRequestAttributes(requestAttributes);
System.out.println("address:"+ Thread.currentThread().getId());
//遠(yuǎn)程獲取地址信息
List<MemberAddressVo> address = memberFeignService.getAddress(member.getId());
orderConfirmVo.setAddress(address);
}, executor);
CompletableFuture<Void> getItem = CompletableFuture.runAsync(() -> {
//子線程中設(shè)置添加主線程的請(qǐng)求頭信息 信息共享 否則遠(yuǎn)程調(diào)用異步處理丟失請(qǐng)求頭信息
RequestContextHolder.setRequestAttributes(requestAttributes);
System.out.println("item:"+ Thread.currentThread().getId());
//遠(yuǎn)程獲取購(gòu)物項(xiàng)
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令牌儲(chǔ)存在redis
String token = UUID.randomUUID().toString().replace("-", "");
orderConfirmVo.setOrderToken(token);
redisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN+member.getId(),token);
return orderConfirmVo;
}總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
用Maven插件生成Mybatis代碼的實(shí)現(xiàn)方法
本文主要介紹 Maven插件生成Mybatis代碼,現(xiàn)在做開發(fā)的朋友有好多用Maven 來(lái)管理代碼,這里給大家舉個(gè)例子,有需要的同學(xué)可以看下2016-07-07
Java數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組(動(dòng)力節(jié)點(diǎn)之Java學(xué)院整理)
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組(動(dòng)力節(jié)點(diǎn)之Java學(xué)院整理)的相關(guān)資料,包括創(chuàng)建和內(nèi)存分配,數(shù)組封裝后的使用等,需要的朋友參考下吧2017-04-04
詳解如何在SpringBoot項(xiàng)目中使用統(tǒng)一返回結(jié)果
在一個(gè)完整的項(xiàng)目中,如果每一個(gè)控制器的方法都返回不同的結(jié)果,那么對(duì)項(xiàng)目的維護(hù)和擴(kuò)展都會(huì)很麻煩。因此,本文為大家準(zhǔn)備了SpringBoot項(xiàng)目中使用統(tǒng)一返回結(jié)果的方法,需要的可以參考一下2022-10-10
Java中數(shù)據(jù)庫(kù)常用的兩把鎖之樂觀鎖和悲觀鎖
這篇文章主要介紹了數(shù)據(jù)庫(kù)常用的兩把鎖之樂觀鎖和悲觀鎖,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Spring中的10種事務(wù)失效的常見場(chǎng)景
這篇文章主要介紹了Spring中的10種事務(wù)失效的常見場(chǎng)景,Spring的聲明式事務(wù)功能更是提供了極其方便的事務(wù)配置方式,配合Spring Boot的自動(dòng)配置,大多數(shù)Spring Boot項(xiàng)目只需要在方法上標(biāo)記@Transactional注解,即可一鍵開啟方法的事務(wù)性配置,需要的朋友可以參考下2023-11-11

