項目中發現了一個新的玩意WebAsyncTask
本文轉載自微信公眾號「六脈神劍的程序人生」,作者六脈神劍小六六。轉載本文請聯系六脈神劍的程序人生公眾號。
絮叨
剛好在讀項目代碼的時候,發現了WebAsyncTask這個新玩意,給大家來科普科普,不是那么的深入,不喜勿噴!
SpringBoot中同異步調用的使用
異步請求的處理。除了異步請求,一般上我們用的比較多的應該是異步調用。通常在開發過程中,會遇到一個方法是和實際業務無關的,沒有緊密性的。比如記錄日志信息等業務。這個時候正常就是啟一個新線程去做一些業務處理,讓主線程異步的執行其他業務。
- 同步請求
- 異步請求
SprinBoot中@Async異步方法
異步的好處是,可以提高程序吞吐量,一個任務,讓耗時的異步處理,并繼續同步處理后面的任務,異步任務可以返回結果,拿到結果后可結合同步處理過程中的變量一起處理計算
具體的使用
在Spring中,基于@Async標注的方法,稱之為異步方法;這些方法將在執行的時候,將會在獨立的線程中被執行,調用者無需等待它的完成,即可繼續其他的操作。
自定義線程池異步調用
配置@EnableAsync使@Async生效
- @SpringBootApplication
- @EnableAsync
- public class Application {
- public static void main(String[] args) {
- SpringApplication.run(Application.class, args);
- }
- }
自定義線程池
- @Component
- @Scope //單例
- public class MyExecutePoll {
- @Bean
- public Executor myAsyncPool() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- //核心線程池大小
- executor.setCorePoolSize(20);
- //最大線程數
- executor.setMaxPoolSize(40);
- //隊列容量
- executor.setQueueCapacity(50);
- // 活躍時間
- executor.setKeepAliveSeconds(300);
- // 線程名字前綴
- executor.setThreadNamePrefix("MyExecutor-");
- //設置線程池關閉的時候等待所有任務都完成再繼續銷毀其他的Bean,使異步線程的銷毀優先于Redis等其他處理報錯
- executor.setWaitForTasksToCompleteOnShutdown(true);
- //設置線程池中任務的等待時間,如果超過這個時候還沒有銷毀就強制銷毀,以確保應用最后能夠被關閉,而不是阻塞住
- executor.setAwaitTerminationSeconds(60);
- // setRejectedExecutionHandler:當pool已經達到max size的時候,如何處理新任務
- // CallerRunsPolicy:不在新線程中執行任務,而是由調用者所在的線程來執行
- executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
- executor.initialize();
- return executor;
- }
- }
使用@Async
- @Async("myAsyncPool") //@Async使用默認的線程
- public Future<String> doTask() throws Exception {
- //業務處理 使用Future返回異步調用結果
- return new AsyncResult<>("任務一完成");
在Spring中運用 Async注解 需要注意幾點:
- AsyncTest.java,測試類,調用異步任務,同時執行同步方法
- OrderService.java,異步任務類,提供異步方法
- AsyncThreadPoolConfig.java,異步任務線程池配置類,配置異步任務運行的線程池大小等
基于Spring實現異步請求
Spring可以通過Callable或者WebAsyncTask等方式實現異步請求, 我們來看看,這2種實現方式!
Callable
Callable是為了異步生成返回值提供基本的支持。簡單來說就是一個請求進來,如果你使用了Callable,在沒有得到返回數據之前,DispatcherServlet和所有Filter就會退出Servlet容器線程,但響應保持打開狀態,一旦返回數據有了,這個DispatcherServlet就會被再次調用并且處理,以異步產生的方式,向請求端返回值。這么做的好處就是請求不會長時間占用服務連接池,提高服務器的吞吐量。
- @GetMapping("/callable")
- public Callable<String> testCallable() throws InterruptedException {
- log.info("主線程開始!");
- Callable<String> result = new Callable<String>() {
- @Override
- public String call() throws Exception {
- log.info("副線程開始!");
- Thread.sleep(1000);
- log.info("副線程結束!");
- return "SUCCESS";
- }
- };
- log.info("主線程結束!");
- return result;
- }
輸出結果
- 主線程開始!
- 主線程結束!
- 副線程開始!
- 副線程結束!
WebAsyncTask
一個請求到服務上,是用的web容器的線程接收的
我們可以使用WebAsyncTask將這個請求分發給一個新的線程去執行,容器的線程可以去接收其他請求的處理。一旦WebAsyncTask返回數據有了,就會被再次調用并且處理,以異步產生的方式,向請求端返回值,但是其實我覺得前端的請求rt并不會說變短。
- /**
- * 查詢
- */
- @RequestMapping(method = RequestMethod.GET, value = "/aysncTask/{testId}")
- @ResponseStatus(HttpStatus.OK)
- public WebAsyncTask<Response> aysncTask(@PathVariable("testId") String testId) {
- System.out.println(String.format("/aysncTask/%s 被調用 thread id is: %s", testId,Thread.currentThread().getName()));
- Callable<Response> callable = () -> {
- Thread.sleep(1000L);
- Response response = new Response(true,"異步執行成功");
- System.out.println(String.format("/aysncTask/%s 被調用 thread id is: %s", testId,Thread.currentThread().getName()));
- return response;
- };
- return new WebAsyncTask<Response>(callable);
- }
控制臺打印如下:在執行業務邏輯之前的線程和具體處理業務邏輯的線程不是同一個,達到了我們的目的。async-customize-1這個前綴是我們自定義的下邊會說
- /aysncTask/12348567676 被調用 thread id is: http-nio-8084-exec-1
- /aysncTask/12348567676 被調用 thread id is: async-customize-1
其實WebAsyncTask比起Callable是有以下幾個優點的
官方有這么一句話,截圖給你:
如果我們需要超時處理的回調或者錯誤處理的回調,我們可以使用WebAsyncTask代替Callable
實際使用中,我并不建議直接使用Callable ,而是使用Spring提供的WebAsyncTask 代替,它包裝了Callable,功能更強大些
總結
其實本文就是給大家科普下,一些異步的用法,不至于說看到人家這么用很蒙b,多線程的東西還是優點東西的,大家一起學習。