<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          5種SpringMvc的異步處理方式你都了解嗎?

          共 6930字,需瀏覽 14分鐘

           ·

          2020-12-24 12:20

          點(diǎn)擊藍(lán)色字關(guān)注我們!



          引言

          說(shuō)到異步大家肯定首先會(huì)先想到同步。我們先來(lái)看看什么是同步?所謂同步,就是發(fā)出一個(gè)功能調(diào)用時(shí),在沒(méi)有得到結(jié)果之前,該調(diào)用就不返回或繼續(xù)執(zhí)行后續(xù)操作。簡(jiǎn)單來(lái)說(shuō),同步就是必須一件一件事做,等前一件做完了才能做下一件事。異步:異步就相反,調(diào)用在發(fā)出之后,這個(gè)調(diào)用就直接返回了,不需要等結(jié)果。

          瀏覽器同步

          瀏覽器發(fā)起一個(gè)request然后會(huì)一直待一個(gè)響應(yīng)response,在這期間里面它是阻塞的。 比如早期我們?cè)谖覀冊(cè)诠潆娚唐脚_(tái)的時(shí)候買東西我們打開(kāi)一個(gè)商品的頁(yè)面,大致流程是不是可能是這樣,每次打開(kāi)一個(gè)頁(yè)面都是由一個(gè)線程從頭到尾來(lái)處理,這個(gè)請(qǐng)求需要進(jìn)行數(shù)據(jù)庫(kù)的訪問(wèn)需要把商品價(jià)格庫(kù)存啥的返回頁(yè)面,還需要去調(diào)用第三方接口,比如優(yōu)惠券接口等我們只有等到這些都處理完成后這個(gè)線程才會(huì)把結(jié)果響應(yīng)給瀏覽器,在這等結(jié)果期間這個(gè)線程只能一直在干等著啥事情也不能干。這樣的話是不是會(huì)有有一定的性能問(wèn)題。大致的流程如下:

          瀏覽器異步

          為了解決上面同步阻塞的問(wèn)題,再Servlet3.0發(fā)布后,提供了一個(gè)新特性:異步處理請(qǐng)求。比如我們還是進(jìn)入商品詳情頁(yè)面,這時(shí)候這個(gè)前端發(fā)起一個(gè)請(qǐng)求,然后會(huì)有一個(gè)線程來(lái)執(zhí)行這個(gè)請(qǐng)求,這個(gè)請(qǐng)求需要去數(shù)據(jù)庫(kù)查詢庫(kù)存、調(diào)用第三方接口查詢優(yōu)惠券等。這時(shí)候這個(gè)線程就不用干等著呢。它的任務(wù)到這就完成了,又可以執(zhí)行下一個(gè)任務(wù)了。等查詢數(shù)據(jù)庫(kù)和第三方接口查詢優(yōu)惠券有結(jié)果了,這時(shí)候會(huì)有一個(gè)新的線程來(lái)把處理結(jié)果返回給前端。這樣的話線程的工作量是不超級(jí)飽和,需要不停的干活,連休息的機(jī)會(huì)都不給了。

          在這里插入圖片描述
          • 這個(gè)異步是純后端的異步,對(duì)前端是無(wú)感的,異步也并不會(huì)帶來(lái)響應(yīng)時(shí)間上的優(yōu)化,原來(lái)該執(zhí)行多久照樣還是需要執(zhí)行多久。但是我們的請(qǐng)求線程(Tomcat 線程)為異步servlet之后,我們可以立即返回,依賴于業(yè)務(wù)的任務(wù)用業(yè)務(wù)線程來(lái)執(zhí)行,也就是說(shuō),Tomcat的線程可以立即回收,默認(rèn)情況下,Tomcat的核心線程是10,最大線程數(shù)是200,我們能及時(shí)回收線程,也就意味著我們能處理更多的請(qǐng)求,能夠增加我們的吞吐量,這也是異步Servlet的主要作用。下面我們就來(lái)看看Spring mvc 的幾種異步方式吧 https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-ann-async在這個(gè)之前我們還是先簡(jiǎn)單的回顧下Servlet 3.1的異步:
          • 客戶端(瀏覽器、app)發(fā)送一個(gè)請(qǐng)求
          • Servlet容器分配一個(gè)線程來(lái)處理容器中的一個(gè)servlet
          • servlet調(diào)用request.startAsync()開(kāi)啟異步模式,保存AsyncContext, 然后返回。
          • 這個(gè)servlet請(qǐng)求線程以及所有的過(guò)濾器都可以結(jié)束,但其響應(yīng)(response)會(huì)等待異步線程處理結(jié)束后再返回。
          • 其他線程使用保存的AsyncContext來(lái)完成響應(yīng)
          • 客戶端收到響應(yīng)

          Callable

          ?/**??公眾號(hào):java金融
          ?????*?使用Callable
          ?????*?@return
          ?????*/

          ????@GetMapping("callable")
          ????public?Callable?callable()?{
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程開(kāi)始");
          ????????Callable?callable?=?()?->?{
          ????????????String?result?=?"return?callable";
          ????????????//?執(zhí)行業(yè)務(wù)耗時(shí)?5s
          ????????????Thread.sleep(5000);
          ????????????System.out.println(LocalDateTime.now().toString()?+?"--->子任務(wù)線程("+Thread.currentThread().getName()+")");
          ????????????return?result;
          ????????};
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程結(jié)束");
          ????????return?callable;
          ????}
          ???????public?static?String?doBusiness()?{
          ????????//?執(zhí)行業(yè)務(wù)耗時(shí)?10s
          ????????try?{
          ????????????Thread.sleep(10000);
          ????????}?catch?(InterruptedException?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????????return?UUID.randomUUID().toString();
          ????}
          • 控制器先返回一個(gè)Callable對(duì)象
          • Spring MVC開(kāi)始進(jìn)行異步處理,并把該Callable對(duì)象提交給另一個(gè)獨(dú)立線程的執(zhí)行器TaskExecutor處理
          • DispatcherServlet和所有過(guò)濾器都退出Servlet容器線程,但此時(shí)方法的響應(yīng)對(duì)象仍未返回
          • Callable對(duì)象最終產(chǎn)生一個(gè)返回結(jié)果,此時(shí)Spring MVC會(huì)重新把請(qǐng)求分派回Servlet容器,恢復(fù)處理
          • DispatcherServlet再次被調(diào)用,恢復(fù)對(duì)Callable異步處理所返回結(jié)果的處理 上面就是Callable的一個(gè)執(zhí)行流程,下面我們來(lái)簡(jiǎn)單的分析下源碼,看看是怎么實(shí)現(xiàn)的:我們知道SpringMvc是可以返回json格式數(shù)據(jù)、或者返回視圖頁(yè)面(html、jsp)等,SpringMvc是怎么實(shí)現(xiàn)這個(gè)的呢?最主要的一個(gè)核心類就是org.springframework.web.method.support.HandlerMethodReturnValueHandler 我們來(lái)看看這個(gè)類,這個(gè)類就是一個(gè)接口,總共就兩個(gè)方法;
          boolean?supportsReturnType(MethodParameter?returnType);
          void?handleReturnValue(@Nullable?Object?returnValue,?MethodParameter?returnType,ModelAndViewContainer?mavContainer,?NativeWebRequest?webRequest)?throws?Exception;

          上面這個(gè)我們的請(qǐng)求是返回Callable這樣一個(gè)結(jié)果的,我們會(huì)根據(jù)這個(gè)返回的類型去找所有實(shí)現(xiàn)了HandlerMethodReturnValueHandler 這個(gè)接口的實(shí)現(xiàn)類,最終我們會(huì)根據(jù)返回類型通過(guò)supportsReturnType這個(gè)實(shí)現(xiàn)的方法找到一個(gè)對(duì)應(yīng)的HandlerMethodReturnValueHandler 實(shí)現(xiàn)類,我們根據(jù)返回類型是Callable然后就找到了實(shí)現(xiàn)類CallableMethodReturnValueHandler。開(kāi)啟異步線程的話也就是在handleReturnValue這個(gè)方法里面了,感興趣的大家可以動(dòng)手去debug下還是比較好調(diào)試的。

          CompletableFuture ?和ListenableFuture

          ???@GetMapping("completableFuture")
          ????public?CompletableFuture?completableFuture()?{
          ????????//?線程池一般不會(huì)放在這里,會(huì)使用static聲明,這只是演示
          ????????ExecutorService?executor?=?Executors.newCachedThreadPool();
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程開(kāi)始");
          ????????CompletableFuture?completableFuture?=?CompletableFuture.supplyAsync(IndexController::doBusiness,?executor);
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程結(jié)束");
          ????????return?completableFuture;
          ????}

          ????@GetMapping("listenableFuture")
          ????public?ListenableFuture?listenableFuture()?{
          ????????//?線程池一般不會(huì)放在這里,會(huì)使用static聲明,這只是演示
          ????????ExecutorService?executor?=?Executors.newCachedThreadPool();
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程開(kāi)始");
          ????????ListenableFutureTask?listenableFuture?=?new?ListenableFutureTask<>(()->???doBusiness());
          ????????executor.execute(listenableFuture);
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程結(jié)束");
          ????????return?listenableFuture;
          ????}

          注:這種方式記得不要使用內(nèi)置的不要使用內(nèi)置的 ForkJoinPool線程池,需要自己創(chuàng)建線程池否則會(huì)有性能問(wèn)題

          WebAsyncTask

          ?@GetMapping("asynctask")
          ????public?WebAsyncTask?asyncTask()?{
          ????????SimpleAsyncTaskExecutor?executor?=?new?SimpleAsyncTaskExecutor();
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程開(kāi)始");
          ????????WebAsyncTask?task?=?new?WebAsyncTask(1000L,?executor,?()->?doBusiness());
          ????????task.onCompletion(()->{
          ????????????System.out.println(LocalDateTime.now().toString()?+?"--->調(diào)用完成");
          ????????});
          ????????task.onTimeout(()->{
          ????????????System.out.println("onTimeout");
          ????????????return?"onTimeout";
          ????????});
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程結(jié)束");
          ????????return?task;
          ????}

          DeferredResult

          ????@GetMapping("deferredResult")
          ????public?DeferredResult?deferredResult()?{
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程("+Thread.currentThread().getName()+")開(kāi)始");
          ????????DeferredResult?deferredResult?=?new?DeferredResult<>();
          ????????CompletableFuture.supplyAsync(()->?doBusiness(),?Executors.newFixedThreadPool(5)).whenCompleteAsync((result,?throwable)->{
          ????????????if?(throwable!=null)?{
          ????????????????deferredResult.setErrorResult(throwable.getMessage());
          ????????????}else?{
          ????????????????deferredResult.setResult(result);
          ????????????}
          ????????});
          ????????//?異步請(qǐng)求超時(shí)時(shí)調(diào)用
          ????????deferredResult.onTimeout(()->{
          ????????????System.out.println(LocalDateTime.now().toString()?+?"--->onTimeout");
          ????????});
          ????????//?異步請(qǐng)求完成后調(diào)用
          ????????deferredResult.onCompletion(()->{
          ????????????System.out.println(LocalDateTime.now().toString()?+?"--->onCompletion");
          ????????});
          ????????System.out.println(LocalDateTime.now().toString()?+?"--->主線程("+Thread.currentThread().getName()+")結(jié)束");
          ????????return?deferredResult;
          ????}
          • 上面這幾種異步方式都是會(huì)等到業(yè)務(wù)doBusiness執(zhí)行完之后(10s)才會(huì)把response給到前端,執(zhí)行請(qǐng)求的主線程會(huì)立即結(jié)束,響應(yīng)結(jié)果會(huì)交給另外的線程來(lái)返回給前端。
          • 這種異步跟下面的這個(gè)所謂的假異步是不同的,這種情況是由主線程執(zhí)行完成之后立馬返回值(主線程)給前端,不會(huì)等個(gè)5s在返回給前端。
          ????@GetMapping("call")
          ????public?String?call()?{
          ???????new?Thread(new?Runnable()?{
          ???????????@Override
          ???????????public?void?run()?{
          ???????????????try?{
          ???????????????????Thread.sleep(5000);
          ???????????????}?catch?(InterruptedException?e)?{
          ???????????????????e.printStackTrace();
          ???????????????}
          ???????????}
          ???????}).start();
          ????????return?"這是個(gè)假異步";
          ????}

          這幾種異步方式都跟返回Callable 差不多,都有對(duì)應(yīng)的HandlerMethodReturnValueHandler 實(shí)現(xiàn)類,無(wú)非就是豐富了自己一些特殊的api、比如超時(shí)設(shè)置啥的,以及線程池的創(chuàng)建是誰(shuí)來(lái)創(chuàng)建,執(zhí)行流程基本都是一樣的。

          總結(jié)

          • 了解spring mvc 的異步編程,對(duì)我們后續(xù)學(xué)習(xí)響應(yīng)式編程、rxjava、webflux等都是有好處的。
          • 異步編程可以幫我們高效的利用系統(tǒng)資源。

          結(jié)束

          • 由于自己才疏學(xué)淺,難免會(huì)有紕漏,假如你發(fā)現(xiàn)了錯(cuò)誤的地方,還望留言給我指出來(lái),我會(huì)對(duì)其加以修正。
          • 如果你覺(jué)得文章還不錯(cuò),你的轉(zhuǎn)發(fā)、分享、贊賞、點(diǎn)贊、留言就是對(duì)我最大的鼓勵(lì)。
          • 感謝您的閱讀,十分歡迎并感謝您的關(guān)注。

          站在巨人的肩膀上摘蘋果: https://blog.csdn.net/f641385712/article/details/88692534

          ????????????????????????



          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊(cè),覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫(kù)、數(shù)據(jù)結(jié)構(gòu)、等等。

          獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù) 666?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 105
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一本高清无码 | 蜜桃媒AV成人片免费看 | 亚洲视频完整版在线观看 | 夜色福利在线播放 | 国产999高清无码精品导航 |