欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Springboot配置@Async無效的解決方案

 更新時間:2023年09月27日 09:57:50   作者:唐宋xy  
這篇文章主要介紹了Springboot配置@Async無效的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教

Springboot配置@Async無效

Springboot中為了同時執(zhí)行多個任務(wù),或者同時查詢,加快查詢則使用多線程查詢,在springboot中,可以直接使用 @Async注解來實現(xiàn)異步任務(wù),可以參考Springboot使用@Async實現(xiàn)異步任務(wù)簡單快捷使用,但是異步任務(wù)在某些情況下居然無效。

配置正確,代碼不報錯,但是無法使用多線程異步任務(wù),可能是下面的原因?qū)е拢?/strong>

1.沒有在springboot啟動類當中添加注解 @EnableAsync注解。

2.異步方法使用注解@Async的返回值只能為void或者Future。

3.沒有走Spring的代理類。

因為@Transactional和@Async注解的實現(xiàn)都是基于Spring的AOP,而AOP的實現(xiàn)是基于動態(tài)代理實現(xiàn)的。

那么注解失效的原因就很明顯了,有可能因為調(diào)用方法的是對象本身而不是代理對象,沒有經(jīng)過Spring容器,無法使用代理對象調(diào)用。

4.注解的方法必須是 public 方法。編譯時非public方法會報錯

5.如果需要從 類的內(nèi)部 調(diào)用,需要先獲取其代理類

先注入ApplicationContext

@Autowired
private ApplicationContext applicationContext;

然后在方法中通過ApplicationContext對象獲取當前類對象

applicationContext.getBean(Class);

6.調(diào)用的是靜態(tài)(static )方法,也會導致注解失效,因為注解是基于代理對象調(diào)用,而static方法是屬于類的,無法通過spring的代理對象直接調(diào)用

Springboot @Async失效的坑

異步應(yīng)用場景

為了提高接口的響應(yīng)性能,當業(yè)務(wù)非常復雜的情況下,可以將一部分跟業(yè)務(wù)關(guān)聯(lián)性不是特別強的邏輯進行異步處理。如日志記錄、短信發(fā)送、增加積分等。

通常而言會將此類業(yè)務(wù)邏輯通過異步的方式進行處理,從而加快接口的響應(yīng)速度,常用的解決方案有:

  • 使用JDK 自定義線程池 讓代碼異步執(zhí)行
  • 在springboot 中 使用@Async注解進行異步處理
  • 使用中間件如mq 消息通知讓下游異步消費 如RocketMQ、KAFKA

使用第一種方式,需要精通線程池運行原理,結(jié)合實際的業(yè)務(wù)場景對隊列大小進行合理的設(shè)置。隊列設(shè)置過大過小都會存在內(nèi)存溢出的風險。

第三種方式是最合理的方式,它能夠通過MQ進行削峰填谷,通過合理的參數(shù)配置,保證數(shù)據(jù)不會丟失。但是架構(gòu)改動過大,對小型的單體應(yīng)用來講,工作量過大,成本過高。

在springboot 大行其道的情況下,考慮開發(fā)成本,以及項目時間關(guān)系選用第二種方式來解決代碼異步執(zhí)行的問題。

真實業(yè)務(wù)場景

線上問題

一個工單的分頁列表,前端控制了每個列表最大的顯示條數(shù)為100條。在業(yè)務(wù)流程中存在工單轉(zhuǎn)移的操作,轉(zhuǎn)移一筆工單至少包含以下幾個重要的步驟:

  • 新增工單處理日志,如什么時間點將工單轉(zhuǎn)移給某人
  • 修改工單當前處理人
  • 發(fā)送企業(yè)微信給B端的跟進人(轉(zhuǎn)移人)
  • 發(fā)送im信息給C端的用戶

由于公司采用微服務(wù)架構(gòu),因此每個業(yè)務(wù)模塊拆分的很細,在上述步驟中需要從其他系統(tǒng)中通過rpc調(diào)用接口拿到需要的數(shù)據(jù)才能完成整個業(yè)務(wù)流程數(shù)據(jù)的拼裝,如需要從crm系統(tǒng)拿到組織架構(gòu)信息,獲取轉(zhuǎn)移人的組織架構(gòu)、需要從udb用戶中心獲取轉(zhuǎn)移人的企業(yè)微信昵稱等。

因此在批量轉(zhuǎn)移的時候,前端會出現(xiàn)調(diào)用超時的問題,原因是dubbo接口默認的超時時間是15秒,由于業(yè)務(wù)復雜,導致在15秒內(nèi)執(zhí)行不完業(yè)務(wù)邏輯。

解決方案

  • 將sql處理改為批量執(zhí)行,如新增處理日志 (batch insert);修改當前工單,使用case when 的方式一次性修改完成(批量update)
  • 將發(fā)送消息改成異步處理 加快前端接口的響應(yīng)速度
  • 讓接口提供方提供批量查詢的接口,避免rpc 循環(huán)調(diào)用在網(wǎng)絡(luò)上的消耗

優(yōu)化完成之后,接口的響應(yīng)速度由15秒多,變成了1秒。但是過程中遇到坑了。特此記錄一下

技術(shù)實現(xiàn)

優(yōu)化過程

@Async 注解定義為可以放置在方法上和類上,當使用在類上表明類所有的方法都能異步執(zhí)行。

在Springboot中是需要在方法上加上該注解就可以完美的實現(xiàn)異步執(zhí)行。

原始方法偽代碼如下

/**
 * 原始代碼 采用流水式的代碼一步步實現(xiàn) 業(yè)務(wù)邏輯
 */
public void doBusiness(Object args){
  //1.  新增工單處理日志,如什么時間點將工單轉(zhuǎn)移給某人
  //2. 修改工單當前處理人
  //3. 發(fā)送企業(yè)微信給B端的跟進人(轉(zhuǎn)移人)
  //4. 發(fā)送im信息給C端的用戶
}

那么異步問題就很好處理了,只需要將方法抽離形成多個子方法, 每個方法執(zhí)行自己的業(yè)務(wù)處理邏輯,然后再方法加上@Async注解不就ok了么,偽代碼如下

public void doBusiness(Object args){
 	//2. 修改工單當前處理人
  this.doAsyncBusiness();
}
// 單獨抽離一個異步執(zhí)行的方法 加上@Async注解
@Async
private void doAsyncBusiness(Object args){
  //1.  新增工單處理日志,如什么時間點將工單轉(zhuǎn)移給某人
  //3. 發(fā)送企業(yè)微信給B端的跟進人(轉(zhuǎn)移人)
  //4. 發(fā)送im信息給C端的用戶
}

打完收工,重啟應(yīng)用,進行測試,然而并沒有像預(yù)期中的那樣,接口的響應(yīng)速度還是15秒左右。接著排查原因,可以肯定的是@Async是可以提供異步方法執(zhí)行。應(yīng)該是我們使用方式不對導致。

@Async 限制

熟悉Springboot AOP的同學可能會發(fā)現(xiàn)更改后的代碼存在明顯的問題

  • 首先AOP代理機制要求 被代理的方法必須是 public , private 方法不能被代理
  • 其次AOP代理機制會生成一個代理類 執(zhí)行代理方法 注意 this.doAsyncBusiness() 調(diào)用的是本對象的方法 ;
  • 在啟動類上加上@EnableAsync注解

綜上所述,原因我們已經(jīng)通過AOP代理的原理找到了。下面摘自官方文檔的一段話:

  • it must be applied to public methods only
  • self-invocation – calling the async method from within the same class – won’t work

The reasons are simple – 「the method needs to be public」 so that it can be proxied. And 「self-invocation doesn’t work」 because it bypasses the proxy and calls the underlying method directly.

總結(jié)

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論