<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>

          SpringMVC異步處理太強(qiáng)大了,系統(tǒng)性能大幅提升 | SpringMVC系列第10篇

          共 5479字,需瀏覽 11分鐘

           ·

          2021-07-28 23:12

          大家好,我是【路人甲 Java】號(hào)主路人,本文如果對(duì)你有幫助,點(diǎn)個(gè)在看,順便忙轉(zhuǎn)發(fā)一下,非常需要大家的支持,對(duì) java 有興趣的朋友歡迎加我微信 itsoku 交流。

          1、本篇內(nèi)容

          本文讓大家掌握 springmvc 中異步處理請(qǐng)求,特別牛逼的一個(gè)功能,大家一定要掌握。

          看本文之前,需要一些預(yù)備知識(shí),大家先看下這篇文章:servlet3.0 中的異步處理

          2、看段代碼,分析問(wèn)題

          @ResponseBody
          @RequestMapping("/async/m1.do")
          public String m1() throws InterruptedException {
              long st = System.currentTimeMillis();
              System.out.println("主線程:" + Thread.currentThread() + "," + st + ",開(kāi)始");
              //休眠3秒,模擬耗時(shí)的業(yè)務(wù)操作
              TimeUnit.SECONDS.sleep(3);
              long et = System.currentTimeMillis();
              System.out.println("主線程:" + Thread.currentThread() + "," + st + ",結(jié)束,耗時(shí)(ms):" + (et - st));
              return "ok";
          }

          這段代碼很簡(jiǎn)單

          • 這段代碼是 springmvc 提供的一個(gè)接口

          • 內(nèi)部休眠了 3 秒鐘,用來(lái)模擬耗時(shí)的操作

          • 方法內(nèi)部有 2 條日志(日志中包含了當(dāng)前線程、開(kāi)始時(shí)間、結(jié)束時(shí)間、耗時(shí))

          瀏覽器中訪問(wèn)下這個(gè)接口,效果如下,可以看到接口耗時(shí) 3s 左右。

          控制臺(tái)輸出

          主線程:Thread[http-nio-8080-exec-1,5,main],1624889293055,開(kāi)始
          主線程:Thread[http-nio-8080-exec-1,5,main],1624889293055,結(jié)束,耗時(shí)(ms):3002

          從輸出中,我們可以看出,這個(gè)接口從開(kāi)始到結(jié)束都是由 tomcat 中的線程來(lái)處理用戶請(qǐng)求的,也就是說(shuō),3 秒這段時(shí)間內(nèi),tomcat 中的一個(gè)線程會(huì)被當(dāng)前請(qǐng)求一直占用了則,tomcat 線程是有最大值的,默認(rèn)情況下好像是 75,那么問(wèn)題來(lái)了。

          當(dāng) 3 秒之內(nèi),來(lái)的請(qǐng)求數(shù)量超過(guò)了 tomcat 最大線程數(shù)的時(shí)候,其他請(qǐng)求就無(wú)法處理了,而此時(shí) tomcat 中這些線程都處理 sleep 3s 的休眠狀態(tài),cpu 此時(shí)沒(méi)活干,此時(shí)就會(huì)造成機(jī)器沒(méi)活干,但是呢又不能處理新的請(qǐng)求,這就是坑啊,浪費(fèi)資源,怎么辦呢?

          遇到這種場(chǎng)景的,也就是說(shuō)接口內(nèi)部比價(jià)耗時(shí),但是又不能充分利用 cpu 的,我們可以采用異步的方式來(lái)處理請(qǐng)求,過(guò)程如下:

          tomcat 線程,將請(qǐng)求轉(zhuǎn)發(fā)給我們自定義的子線程去處理這個(gè)請(qǐng)求,然后 tomcat 就可以繼續(xù)去接受新的請(qǐng)求了。

          3、springmvc 中異步處理

          主要有 3 個(gè)大的步驟。

          step1:servlet 開(kāi)啟異步處理支持

          web.xml 中開(kāi)啟 servlet 異步支持

          step2:Filter 中添加異步支持

          如果我們的異步請(qǐng)求需要經(jīng)過(guò) Filter 的,那么需要在 web.xml 對(duì)這個(gè) Filter 添加異步支持.

          step3:接口返回值為 DeferredResult

          這個(gè)步驟中細(xì)節(jié)比較多,當(dāng)需要異步響應(yīng)請(qǐng)求的時(shí)候,返回值需要為 DeferredResult,具體參考下面案例代碼,詳細(xì)信息都在注釋中了,大家注意看注釋。

          • 第 1 步:創(chuàng)建 DeferredResult<返回值類型>(超時(shí)時(shí)間[毫秒],超時(shí)回調(diào)的代碼)
          • 第 2 步:在子線程中異步處理業(yè)務(wù),調(diào)用 DeferredResult 的 setResult 方法,設(shè)置最終返回到客戶端的結(jié)果,此方法調(diào)用以后,客戶端將接收到返回值,然后響應(yīng)過(guò)程請(qǐng)求就結(jié)束了
          • 第 3 步:將 DefaultResult 作為方法返回值
          /**
           * 使用springmvc的異步功能,業(yè)務(wù)處理放在異步線程中執(zhí)行
           *
           * @param timeout 異步處理超時(shí)時(shí)間(毫秒)
           * @return
           */

          @ResponseBody
          @RequestMapping("/async/m2/{timeout}.do")
          public DeferredResult m2(@PathVariable("timeout") long timeout) {
              long st = System.currentTimeMillis();
              System.out.println("主線程:" + Thread.currentThread() + "," + st + ",開(kāi)始");
              /**
               * 1、創(chuàng)建DeferredResult<返回值類型>(超時(shí)時(shí)間[毫秒],超時(shí)回調(diào)的代碼)
               */

              DeferredResult result = new DeferredResult(timeout, () -> {
                  System.out.println("超時(shí)了");
                  return "timeout";
              });
              //2、異步處理業(yè)務(wù),
              new Thread(() -> {
                  //開(kāi)啟一個(gè)異步線程,在異步線程中進(jìn)行業(yè)務(wù)處理操作
                  try {
                      TimeUnit.SECONDS.sleep(3);
                      //3、調(diào)用DeferredResult的setResult方法,設(shè)置最終返回到客戶端的結(jié)果,此方法調(diào)用以后,客戶端將接收到返回值
                      result.setResult("ok");
                  } catch (InterruptedException e) {
                      result.setResult("發(fā)生異常了:" + e.getMessage());
                  }
              }).start();
              long et = System.currentTimeMillis();
              System.out.println("主線程:" + Thread.currentThread() + "," + st + ",結(jié)束,耗時(shí)(ms):" + (et - st));
              //3、將DefaultResult作為方法返回值
              return result;
          }

          上面的 m2 方法個(gè) timeout 參數(shù),調(diào)用者通過(guò)這個(gè)參數(shù)來(lái)指定接口的超時(shí)時(shí)間,未超時(shí)的情況下,也就是說(shuō) timeout 大于 3 秒的時(shí)候,此時(shí)會(huì)輸出 ok,否則將出現(xiàn)超時(shí),此時(shí)會(huì)將 DeferredResult 構(gòu)造器第 2 個(gè)參數(shù)的執(zhí)行結(jié)果作為最終的響應(yīng)結(jié)果,即會(huì)向客戶端輸出 timeout。

          使用建議:案例開(kāi)啟了一個(gè)新的子線程來(lái)執(zhí)行業(yè)務(wù)操作,生產(chǎn)環(huán)境中,建議大家采用線程池的方式,效率更高。

          下面我們來(lái)通過(guò) 2 個(gè) case 來(lái)模擬下這個(gè)接口超時(shí)和正常的結(jié)果。

          4、模擬非超時(shí)請(qǐng)求

          當(dāng) timeout 大于 3 秒時(shí),才不會(huì)出現(xiàn)超時(shí),此時(shí)我們傳遞 4000 毫秒來(lái)試試

          控制臺(tái)輸出如下,可以看到主線程瞬間就結(jié)束了。

          主線程:Thread[http-nio-8080-exec-6,5,main],1624891886020,開(kāi)始
          主線程:Thread[http-nio-8080-exec-6,5,main],1624891886020,結(jié)束,耗時(shí)(ms):0

          5、模擬超時(shí)請(qǐng)求

          當(dāng) timeout 小于 3 秒會(huì)出現(xiàn)超時(shí),此時(shí)我們傳遞 1000 毫秒來(lái)試試

          控制臺(tái)輸出如下,輸出了超時(shí)信息,且通過(guò)前兩行輸出看出主線程瞬間就結(jié)束了,不會(huì)被請(qǐng)求阻塞。

          主線程:Thread[http-nio-8080-exec-1,5,main],1624892109695,開(kāi)始
          主線程:Thread[http-nio-8080-exec-1,5,main],1624892109695,結(jié)束,耗時(shí)(ms):0
          超時(shí)了

          6、總結(jié)

          當(dāng)接口中有大量的耗時(shí)的操作,且這些耗時(shí)的操作讓線程處于等待狀態(tài)時(shí),此時(shí)為了提升系統(tǒng)的性能,可以將接口調(diào)整為異步處理的方式。

          7、案例代碼

          git地址:https://gitee.com/javacode2018/springmvc-series

          8、SpringMVC 系列

          1. SpringMVC 系列第 1 篇:helloword
          2. SpringMVC 系列第 2 篇:@Controller、@RequestMapping
          3. SpringMVC 系列第 3 篇:異常高效的一款接口測(cè)試?yán)?/a>
          4. SpringMVC 系列第 4 篇:controller 常見(jiàn)的接收參數(shù)的方式
          5. SpringMVC 系列第 5 篇:@RequestBody 大解密,說(shuō)點(diǎn)你不知道的
          6. SpringMVC 系列第 6 篇:上傳文件的 4 種方式,你都會(huì)么?

          9、更多好文章

          1. Spring 高手系列(共 56 篇)
          2. Java 高并發(fā)系列(共 34 篇)
          3. MySql 高手系列(共 27 篇)
          4. Maven 高手系列(共 10 篇)
          5. Mybatis 系列(共 12 篇)
          6. 聊聊 db 和緩存一致性常見(jiàn)的實(shí)現(xiàn)方式
          7. 接口冪等性這么重要,它是什么?怎么實(shí)現(xiàn)?
          8. 泛型,有點(diǎn)難度,會(huì)讓很多人懵逼,那是因?yàn)槟銢](méi)有看這篇文章!

          10、推薦一個(gè)高質(zhì)量的公眾號(hào)

          大家平時(shí)在學(xué)習(xí)技術(shù)的過(guò)程中,苦于找不到高質(zhì)量的學(xué)習(xí)資料的,可以關(guān)注一下【Java 充電社】,這個(gè)號(hào)專注于為大家提供高質(zhì)量的學(xué)習(xí)資源,已發(fā)布了大量高質(zhì)量的學(xué)習(xí)視頻、及資源,大家可以關(guān)注下。

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  久爱一区二区 | 污视频在线免费观看一区 | 日韩肏屄视频在线观看 | 华人毛片| 中国产一级黄色a片 |