成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring Boot異步請求和異步調用,一文搞定!

開發 后端
可以先釋放容器分配給請求的線程與相關資源,減輕系統負擔,釋放了容器所分配線程的請求,其響應將被延后,可以在耗時處理完成(例如長時間的運算)時再對客戶端進行響應。

一、Spring Boot中異步請求的使用

1、異步請求與同步請求

特點:

可以先釋放容器分配給請求的線程與相關資源,減輕系統負擔,釋放了容器所分配線程的請求,其響應將被延后,可以在耗時處理完成(例如長時間的運算)時再對客戶端進行響應。

一句話:增加了服務器對客戶端請求的吞吐量(實際生產上我們用的比較少,如果并發請求量很大的情況下,我們會通過nginx把請求負載到集群服務的各個節點上來分攤請求壓力,當然還可以通過消息隊列來做請求的緩沖)。

2、異步請求的實現

方式一:Servlet方式實現異步請求 

  1. @RequestMapping(value = "/email/servletReq"method = GET 
  2.   public void servletReq (HttpServletRequest request, HttpServletResponse response) {  
  3.       AsyncContext asyncContext = request.startAsync();  
  4.       //設置監聽器:可設置其開始、完成、異常、超時等事件的回調處理  
  5.       asyncContext.addListener(new AsyncListener() {  
  6.           @Override  
  7.           public void onTimeout(AsyncEvent event) throws IOException {  
  8.               System.out.println("超時了...");  
  9.               //做一些超時后的相關操作...  
  10.           }  
  11.           @Override  
  12.           public void onStartAsync(AsyncEvent event) throws IOException {  
  13.               System.out.println("線程開始");  
  14.           }  
  15.           @Override  
  16.           public void onError(AsyncEvent event) throws IOException {  
  17.               System.out.println("發生錯誤:"+event.getThrowable());  
  18.           }  
  19.           @Override 
  20.            public void onComplete(AsyncEvent event) throws IOException {  
  21.               System.out.println("執行完成");  
  22.               //這里可以做一些清理資源的操作...  
  23.           }  
  24.       });  
  25.       //設置超時時間  
  26.       asyncContext.setTimeout(20000);  
  27.       asyncContext.start(new Runnable() {  
  28.           @Override  
  29.           public void run() {  
  30.               try {  
  31.                   Thread.sleep(10000);  
  32.                   System.out.println("內部線程:" + Thread.currentThread().getName());  
  33.                   asyncContext.getResponse().setCharacterEncoding("utf-8");  
  34.                   asyncContext.getResponse().setContentType("text/html;charset=UTF-8");  
  35.                   asyncContext.getResponse().getWriter().println("這是異步的請求返回");  
  36.               } catch (Exception e) {  
  37.                   System.out.println("異常:"+e);  
  38.               }  
  39.               //異步請求完成通知  
  40.               //此時整個請求才完成  
  41.               asyncContext.complete();  
  42.           }  
  43.       }); 
  44.        //此時之類 request的線程連接已經釋放了  
  45.       System.out.println("主線程:" + Thread.currentThread().getName());  
  46.   } 

方式二:使用很簡單,直接返回的參數包裹一層callable即可,可以繼承WebMvcConfigurerAdapter類來設置默認線程池和超時處理 

  1. @RequestMapping(value = "/email/callableReq"method = GET 
  2.   @ResponseBody  
  3.   public Callable<String> callableReq () {  
  4.       System.out.println("外部線程:" + Thread.currentThread().getName());  
  5.       return new Callable<String>() {  
  6.           @Override  
  7.           public String call() throws Exception {  
  8.               Thread.sleep(10000);  
  9.               System.out.println("內部線程:" + Thread.currentThread().getName());  
  10.               return "callable!";  
  11.           }  
  12.       };  
  13.   }  
  14.   @Configuration  
  15.   public class RequestAsyncPoolConfig extends WebMvcConfigurerAdapter {  
  16.   @Resource  
  17.   private ThreadPoolTaskExecutor myThreadPoolTaskExecutor;  
  18.   @Override  
  19.   public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {  
  20.       //處理 callable超時  
  21.       configurer.setDefaultTimeout(60*1000);  
  22.       configurer.setTaskExecutor(myThreadPoolTaskExecutor);  
  23.       configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor()); 
  24.   }  
  25.   @Bean  
  26.   public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() {  
  27.       return new TimeoutCallableProcessingInterceptor();  
  28.   }  

方式三:和方式二差不多,在Callable外包一層,給WebAsyncTask設置一個超時回調,即可實現超時處理 

  1. @RequestMapping(value = "/email/webAsyncReq"method = GET 
  2.     @ResponseBody  
  3.     public WebAsyncTask<String> webAsyncReq () {  
  4.         System.out.println("外部線程:" + Thread.currentThread().getName());  
  5.         Callable<String> result = () -> {  
  6.             System.out.println("內部線程開始:" + Thread.currentThread().getName());  
  7.             try {  
  8.                 TimeUnit.SECONDS.sleep(4);  
  9.             } catch (Exception e) {  
  10.                 // TODO: handle exception  
  11.             }  
  12.             logger.info("副線程返回");  
  13.             System.out.println("內部線程返回:" + Thread.currentThread().getName());  
  14.             return "success";  
  15.         };  
  16.         WebAsyncTask<String> wat = new WebAsyncTask<String>(3000L, result);  
  17.         wat.onTimeout(new Callable<String>() {  
  18.             @Override  
  19.             public String call() throws Exception {  
  20.                 // TODO Auto-generated method stub  
  21.                 return "超時";  
  22.             }  
  23.         });  
  24.         return wat;  
  25.     } 

方式四:DeferredResult可以處理一些相對復雜一些的業務邏輯,最主要還是可以在另一個線程里面進行業務處理及返回,即可在兩個完全不相干的線程間的通信。

  1. @RequestMapping(value = "/email/deferredResultReq"method = GET 
  2.     @ResponseBody  
  3.     public DeferredResult<String> deferredResultReq () {  
  4.         System.out.println("外部線程:" + Thread.currentThread().getName());  
  5.         //設置超時時間  
  6.         DeferredResult<String> result = new DeferredResult<String>(60*1000L);  
  7.         //處理超時事件 采用委托機制  
  8.         result.onTimeout(new Runnable() {  
  9.             @Override  
  10.             public void run() {  
  11.                 System.out.println("DeferredResult超時");  
  12.                 result.setResult("超時了!");  
  13.             }  
  14.         });  
  15.         result.onCompletion(new Runnable() {  
  16.             @Override  
  17.             public void run() {  
  18.                 //完成后  
  19.                 System.out.println("調用完成");  
  20.             }  
  21.         });  
  22.         myThreadPoolTaskExecutor.execute(new Runnable() {  
  23.             @Override  
  24.             public void run() {  
  25.                 //處理業務邏輯  
  26.                 System.out.println("內部線程:" + Thread.currentThread().getName());  
  27.                 //返回結果  
  28.                 result.setResult("DeferredResult!!");  
  29.             }  
  30.         });  
  31.        return result;  
  32.     } 

二、Spring Boot中異步調用的使用

1、介紹

異步請求的處理。除了異步請求,一般上我們用的比較多的應該是異步調用。通常在開發過程中,會遇到一個方法是和實際業務無關的,沒有緊密性的。比如記錄日志信息等業務。這個時候正常就是啟一個新線程去做一些業務處理,讓主線程異步的執行其他業務。

2、使用方式(基于spring下)

需要在啟動類加入@EnableAsync使異步調用@Async注解生效

在需要異步執行的方法上加入此注解即可@Async("threadPool"),threadPool為自定義線程池。

代碼略。。。就倆標簽,自己試一把就可以了

3、注意事項

在默認情況下,未設置TaskExecutor時,默認是使用SimpleAsyncTaskExecutor這個線程池,但此線程不是真正意義上的線程池,因為線程不重用,每次調用都會創建一個新的線程。可通過控制臺日志輸出可以看出,每次輸出線程名都是遞增的。所以最好我們來自定義一個線程池。

調用的異步方法,不能為同一個類的方法(包括同一個類的內部類),簡單來說,因為Spring在啟動掃描時會為其創建一個代理類,而同類調用時,還是調用本身的代理類的,所以和平常調用是一樣的。

其他的注解如@Cache等也是一樣的道理,說白了,就是Spring的代理機制造成的。所以在開發中,最好把異步服務單獨抽出一個類來管理。下面會重點講述。。

4、什么情況下會導致@Async異步方法會失效?

調用同一個類下注有@Async異步方法:

在spring中像@Async和@Transactional、cache等注解本質使用的是動態代理,其實Spring容器在初始化的時候Spring容器會將含有AOP注解的類對象“替換”為代理對象(簡單這么理解),那么注解失效的原因就很明顯了,就是因為調用方法的是對象本身而不是代理對象,因為沒有經過Spring容器,那么解決方法也會沿著這個思路來解決。

調用的是靜態(static )方法

調用(private)私有化方法

5、解決4中問題1的方式(其它2,3兩個問題自己注意下就可以了)

將要異步執行的方法單獨抽取成一個類,原理就是當你把執行異步的方法單獨抽取成一個類的時候,這個類肯定是被Spring管理的,其他Spring組件需要調用的時候肯定會注入進去,這時候實際上注入進去的就是代理類了。

其實我們的注入對象都是從Spring容器中給當前Spring組件進行成員變量的賦值,由于某些類使用了AOP注解,那么實際上在Spring容器中實際存在的是它的代理對象。那么我們就可以通過上下文獲取自己的代理對象調用異步方法。 

  1. @Controller  
  2. @RequestMapping("/app")  
  3. public class EmailController {  
  4.     //獲取ApplicationContext對象方式有多種,這種最簡單,其它的大家自行了解一下  
  5.     @Autowired  
  6.     private ApplicationContext applicationContext;  
  7.     @RequestMapping(value = "/email/asyncCall"method = GET 
  8.     @ResponseBody  
  9.     public Map<String, Object> asyncCall () {  
  10.         Map<String, Object> resMap = new HashMap<String, Object>();  
  11.         try{  
  12.             //這樣調用同類下的異步方法是不起作用的  
  13.             //this.testAsyncTask();  
  14.             //通過上下文獲取自己的代理對象調用異步方法  
  15.             EmailController emailController = (EmailController)applicationContext.getBean(EmailController.class);  
  16.             emailController.testAsyncTask();  
  17.             resMap.put("code",200);  
  18.         }catch (Exception e) {  
  19.             resMap.put("code",400);  
  20.             logger.error("error!",e);  
  21.         }  
  22.         return resMap;  
  23.     }   
  24.     //注意一定是public,且是非static方法  
  25.     @Async  
  26.     public void testAsyncTask() throws InterruptedException {  
  27.         Thread.sleep(10000);  
  28.         System.out.println("異步任務執行完成!");  
  29.     }  

開啟cglib代理,手動獲取Spring代理類,從而調用同類下的異步方法。首先,在啟動類上加上@EnableAspectJAutoProxy(exposeProxy = true)注解。代碼實現,如下: 

  1. @Service  
  2. @Transactional(value = "transactionManager"readOnly = falsepropagation = Propagation.REQUIRED, rollbackFor = Throwable.class)  
  3. public class EmailService {  
  4.     @Autowired  
  5.     private ApplicationContext applicationContext;  
  6.     @Async  
  7.     public void testSyncTask() throws InterruptedException {  
  8.         Thread.sleep(10000);  
  9.         System.out.println("異步任務執行完成!");  
  10.     }  
  11.     public void asyncCallTwo() throws InterruptedException {  
  12.         //this.testSyncTask();  
  13. //        EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);  
  14. //        emailService.testSyncTask();  
  15.         boolean isAop = AopUtils.isAopProxy(EmailController.class);//是否是代理對象;  
  16.         boolean isCglib = AopUtils.isCglibProxy(EmailController.class);  //是否是CGLIB方式的代理對象;  
  17.         boolean isJdk = AopUtils.isJdkDynamicProxy(EmailController.class);  //是否是JDK動態代理方式的代理對象;  
  18.         //以下才是重點!!!  
  19.         EmailService emailService = (EmailService)applicationContext.getBean(EmailService.class);  
  20.         EmailService proxy = (EmailService) AopContext.currentProxy();  
  21.         System.out.println(emailService == proxy ? true : false);  
  22.         proxy.testSyncTask();  
  23.         System.out.println("end!!!");  
  24.     }  

三、異步請求與異步調用的區別

兩者的使用場景不同,異步請求用來解決并發請求對服務器造成的壓力,從而提高對請求的吞吐量;而異步調用是用來做一些非主線流程且不需要實時計算和響應的任務,比如同步日志到kafka中做日志分析等。

異步請求是會一直等待response相應的,需要返回結果給客戶端的;而異步調用我們往往會馬上返回給客戶端響應,完成這次整個的請求,至于異步調用的任務后臺自己慢慢跑就行,客戶端不會關心。

四、總結

異步請求和異步調用的使用到這里基本就差不多了,有問題還希望大家多多指出。這邊文章提到了動態代理,而spring中Aop的實現原理就是動態代理,后續會對動態代理做詳細解讀,還望多多支持哈~ 

 

責任編輯:龐桂玉 來源: Java技術棧
相關推薦

2024-07-31 15:57:41

2024-10-15 10:28:43

2025-04-07 08:20:00

ORMPython代碼

2024-08-12 10:13:01

2021-02-17 09:09:15

異步請求

2018-06-21 14:46:03

Spring Boot異步調用

2009-11-09 10:50:30

WCF異步調用

2009-12-21 14:10:26

WCF異步調用

2022-09-27 12:01:56

Spring異步調用方式

2021-09-15 06:55:34

異步LinqC#

2009-10-20 16:48:30

C#委托

2009-11-06 15:54:15

WCF異步調用

2009-07-01 13:58:00

JavaScript異

2021-09-18 16:10:48

Spring BootJava微服務

2009-12-07 14:35:42

WCF異步調用

2010-01-11 17:24:19

VB.NET異步調用

2011-03-02 08:57:22

jQueryJavaScript

2012-10-29 10:59:27

Windows 8

2009-12-07 14:26:47

WCF異步調用

2021-03-29 09:26:44

SpringBoot異步調用@Async
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人av一区二区三区 | 日韩欧美一级精品久久 | 久久精品亚洲精品 | 欧美久久久久久 | 91九色porny首页最多播放 | 成人在线视频网 | 国产精品视频综合 | 久久亚洲一区二区三区四区 | 国产精品一区二区在线 | 天天综合网91 | 国产高清精品一区二区三区 | 国产一二区视频 | 日本中文字幕一区 | 亚洲国产精品va在线看黑人 | 自拍视频国产 | 国产一区二区三区四区 | 日日干夜夜操天天操 | 国产精品久久a | 亚洲精品免费视频 | 天天爽夜夜骑 | 一区二区中文 | 欧美一级欧美一级在线播放 | 综合久久网| 亚洲精品一二三区 | 精产嫩模国品一二三区 | 99久久久无码国产精品 | 在线视频a | 久久精品小视频 | 国产成人精品久久二区二区91 | 亚洲不卡在线视频 | 亚洲成人一区二区在线 | 国产成人精品免费视频大全最热 | 国产精品婷婷 | 欧洲一级视频 | 亚洲精品久久久一区二区三区 | 8x国产精品视频一区二区 | 欧美jizzhd精品欧美巨大免费 | 99热这里都是精品 | 日本在线视频中文字幕 | 国产目拍亚洲精品99久久精品 | 国产精品亚洲精品 |