如何解決異步任務(wù)上下文丟失問題
解決異步任務(wù)上下文丟失問題
- 上下文丟失主要是因為主線程和子線程的上下文不能共享。
- 可以通過執(zhí)行異步任務(wù)之前,將主線程上的上下文信息拷貝到子線程上。
自定義TaskDecorator 來拷貝主線程上的上下文信息到子線程,然后將自定義的 TaskDecorator實現(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)用情況下會有一個嚴(yán)重的問題,丟失上文下
通過以上圖片可以看出異步丟失上下文的原因是不在同一個線程,所有數(shù)據(jù)不能共享,Wie了解決這個問題,我們就需要把之前線程的請求頭上下文,在次存放到其他線程的請求頭上下文就行,具體實現(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()); //獲取主線程的請求頭信息 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); //考慮到效率問題 使用異步編排 CompletableFuture<Void> getAddress = CompletableFuture.runAsync(() -> { //子線程中設(shè)置添加主線程的請求頭信息 信息共享 否則遠(yuǎn)程調(diào)用異步處理丟失請求頭信息 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è)置添加主線程的請求頭信息 信息共享 否則遠(yuǎn)程調(diào)用異步處理丟失請求頭信息 RequestContextHolder.setRequestAttributes(requestAttributes); System.out.println("item:"+ Thread.currentThread().getId()); //遠(yuǎn)程獲取購物項 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)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
用Maven插件生成Mybatis代碼的實現(xiàn)方法
本文主要介紹 Maven插件生成Mybatis代碼,現(xiàn)在做開發(fā)的朋友有好多用Maven 來管理代碼,這里給大家舉個例子,有需要的同學(xué)可以看下2016-07-07Java數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組(動力節(jié)點之Java學(xué)院整理)
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組(動力節(jié)點之Java學(xué)院整理)的相關(guān)資料,包括創(chuàng)建和內(nèi)存分配,數(shù)組封裝后的使用等,需要的朋友參考下吧2017-04-04詳解如何在SpringBoot項目中使用統(tǒng)一返回結(jié)果
在一個完整的項目中,如果每一個控制器的方法都返回不同的結(jié)果,那么對項目的維護(hù)和擴(kuò)展都會很麻煩。因此,本文為大家準(zhǔn)備了SpringBoot項目中使用統(tǒng)一返回結(jié)果的方法,需要的可以參考一下2022-10-10Java中數(shù)據(jù)庫常用的兩把鎖之樂觀鎖和悲觀鎖
這篇文章主要介紹了數(shù)據(jù)庫常用的兩把鎖之樂觀鎖和悲觀鎖,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07