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