最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Spring MVC的異步模式(高性能的關(guān)鍵)

 更新時間:2017年02月04日 15:53:27   作者:蔣國綱  
本篇文章主要介紹了詳解Spring MVC的異步模式(高性能的關(guān)鍵),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

什么是異步模式

要知道什么是異步模式,就先要知道什么是同步模式,先看最典型的同步模式:

瀏覽器發(fā)起請求,Web服務(wù)器開一個線程處理,處理完把處理結(jié)果返回瀏覽器。好像沒什么好說的了,絕大多數(shù)Web服務(wù)器都如此般處理?,F(xiàn)在想想如果處理的過程中需要調(diào)用后端的一個業(yè)務(wù)邏輯服務(wù)器,會是怎樣呢?

調(diào)就調(diào)吧,上圖所示,請求處理線程會在Call了之后等待Return,自身處于阻塞狀態(tài)。這也是絕大多數(shù)Web服務(wù)器的做法,一般來說這樣做也夠了,為啥?一來“長時間處理服務(wù)”調(diào)用通常不多,二來請求數(shù)其實也不多。要不是這樣的話,這種模式會出現(xiàn)什么問題呢?——會出現(xiàn)的問題就是請求處理線程的短缺!因為請求處理線程的總數(shù)是有限的,如果類似的請求多了,所有的處理線程處于阻塞的狀態(tài),那新的請求也就無法處理了,也就所謂影響了服務(wù)器的吞吐能力。要更加好地發(fā)揮服務(wù)器的全部性能,就要使用異步,這也是標題上所說的“高性能的關(guān)鍵”。接下來我們來看看異步是怎么一回事:

最大的不同在于請求處理線程對后臺處理的調(diào)用使用了“invoke”的方式,就是說調(diào)了之后直接返回,而不等待,這樣請求處理線程就“自由”了,它可以接著去處理別的請求,當(dāng)后端處理完成后,會鉤起一個回調(diào)處理線程來處理調(diào)用的結(jié)果,這個回調(diào)處理線程跟請求處理線程也許都是線程池中的某個線程,相互間可以完全沒有關(guān)系,由這個回調(diào)處理線程向瀏覽器返回內(nèi)容。這就是異步的過程。

帶來的改進是顯而易見的,請求處理線程不需要阻塞了,它的能力得到了更充分的使用,帶來了服務(wù)器吞吐能力的提升。

Spring MVC的使用——DefferedResult

要使用Spring MVC的異步功能,你得先確保你用的是Servlet 3.0或以上的版本,Maven中如此配置:

  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>3.1.0</version>
   <scope>provided</scope>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>4.2.3.RELEASE</version>
  </dependency>

我這里使用的Servlet版本是3.1.0,Spring MVC版本是4.2.3,建議使用最新的版本。

由于Spring MVC的良好封裝,異步功能使用起來出奇的簡單。傳統(tǒng)的同步模式的Controller是返回ModelAndView,而異步模式則是返回DeferredResult<ModelAndView>。

看這個例子:

@RequestMapping(value="/asynctask", method = RequestMethod.GET)
public DeferredResult<ModelAndView> asyncTask(){
  DeferredResult<ModelAndView> deferredResult = new DeferredResult<ModelAndView>();
  System.out.println("/asynctask 調(diào)用!thread id is : " + Thread.currentThread().getId());
  longTimeAsyncCallService.makeRemoteCallAndUnknownWhenFinish(new LongTermTaskCallback() {
    @Override
    public void callback(Object result) {
      System.out.println("異步調(diào)用執(zhí)行完成, thread id is : " + Thread.currentThread().getId());
      ModelAndView mav = new ModelAndView("remotecalltask");
      mav.addObject("result", result);
      deferredResult.setResult(mav);
    }
  });
}

longTimeAsyncCallService是我寫的一個模擬長時間異步調(diào)用的服務(wù)類,調(diào)用之,立即返回,當(dāng)它處理完成時候,就鉤起一個線程調(diào)用我們提供的回調(diào)函數(shù),這跟“圖3”描述的一樣,它的代碼如下:

public interface LongTermTaskCallback {
  void callback(Object result);
}

public class LongTimeAsyncCallService {
  private final int CorePoolSize = 4;
  private final int NeedSeconds = 3;
  private Random random = new Random();
  private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(CorePoolSize);
  public void makeRemoteCallAndUnknownWhenFinish(LongTermTaskCallback callback){
    System.out.println("完成此任務(wù)需要 : " + NeedSeconds + " 秒");
    scheduler.schedule(new Runnable() {
      @Override
      public void run() {
        callback.callback("長時間異步調(diào)用完成.");
      }
    }, "這是處理結(jié)果:)", TimeUnit.SECONDS);
  }
}

輸出的結(jié)果是:

/asynctask 調(diào)用!thread id is : 46

完成此任務(wù)需要 : 3 秒

異步調(diào)用執(zhí)行完成, thread id is : 47

由此可見返回結(jié)果的線程和請求處理線程不是同一線程。

還有個叫WebAsyncTask

返回DefferedResult<ModelAndView>并非唯一做法,還可以返回WebAsyncTask來實現(xiàn)“異步”,但略有不同,不同之處在于返回WebAsyncTask的話是不需要我們主動去調(diào)用Callback的,看例子:

@RequestMapping(value="/longtimetask", method = RequestMethod.GET)
public WebAsyncTask longTimeTask(){
  System.out.println("/longtimetask被調(diào)用 thread id is : " + Thread.currentThread().getId());
  Callable<ModelAndView> callable = new Callable<ModelAndView>() {
    public ModelAndView call() throws Exception {
      Thread.sleep(3000); //假設(shè)是一些長時間任務(wù)
      ModelAndView mav = new ModelAndView("longtimetask");
      mav.addObject("result", "執(zhí)行成功");
      System.out.println("執(zhí)行成功 thread id is : " + Thread.currentThread().getId());
      return mav;
    }
  };
  return new WebAsyncTask(callable);
}

其核心是一個Callable<ModelAndView>,事實上,直接返回Callable<ModelAndView>都是可以的,但我們這里包裝了一層,以便做后面提到的“超時處理”。和前一個方案的差別在于這個Callable的call方法并不是我們直接調(diào)用的,而是在longTimeTask返回后,由Spring MVC用一個工作線程來調(diào)用,執(zhí)行,打印出來的結(jié)果:

/longtimetask被調(diào)用 thread id is : 56

執(zhí)行成功 thread id is : 57

可見確實由不同線程執(zhí)行的,但這個WebAsyncTask可不太符合“圖3”所描述的技術(shù)規(guī)格,它僅僅是簡單地把請求處理線程的任務(wù)轉(zhuǎn)交給另一工作線程而已。

處理超時

如果“長時間處理任務(wù)”一直沒返回,那我們也不應(yīng)該讓客戶端無限等下去啊,總歸要弄個“超時”出來。如圖:

 

其實“超時處理線程”和“回調(diào)處理線程”可能都是線程池中的某個線程,我為了清晰點把它們分開畫而已。增加這個超時處理在Spring MVC中非常簡單,先拿WebAsyncTask那段代碼來改一下:

@RequestMapping(value="/longtimetask", method = RequestMethod.GET)
public WebAsyncTask longTimeTask(){
  System.out.println("/longtimetask被調(diào)用 thread id is : " + Thread.currentThread().getId());
  Callable<ModelAndView> callable = new Callable<ModelAndView>() {
    public ModelAndView call() throws Exception {
      Thread.sleep(3000); //假設(shè)是一些長時間任務(wù)
      ModelAndView mav = new ModelAndView("longtimetask");
      mav.addObject("result", "執(zhí)行成功");
      System.out.println("執(zhí)行成功 thread id is : " + Thread.currentThread().getId());
      return mav;
    }
  };
  
  
  WebAsyncTask asyncTask = new WebAsyncTask(2000, callable);
  asyncTask.onTimeout(
      new Callable<ModelAndView>() {
        public ModelAndView call() throws Exception {
          ModelAndView mav = new ModelAndView("longtimetask");
          mav.addObject("result", "執(zhí)行超時");
          System.out.println("執(zhí)行超時 thread id is :" + Thread.currentThread().getId());
          return mav;
        }
      }
  );
  return new WebAsyncTask(3000, callable);
}

注意看紅色字體部分代碼,這就是前面提到的為什么Callable還要外包一層的緣故,給WebAsyncTask設(shè)置一個超時回調(diào),即可實現(xiàn)超時處理,在這個例子中,正常處理需要3秒鐘,而超時設(shè)置為2秒,所以肯定會出現(xiàn)超時,執(zhí)行打印log如下:

/longtimetask被調(diào)用 thread id is : 59

執(zhí)行超時 thread id is :61

執(zhí)行成功 thread id is : 80

嗯?明明超時了,怎么還會“執(zhí)行成功”呢?超時歸超時,超時并不會打斷正常執(zhí)行流程,但注意,出現(xiàn)超時后我們給客戶端返回了“超時”的結(jié)果,那接下來即便正常處理流程成功,客戶端也收不到正常處理成功所產(chǎn)生的結(jié)果了,這帶來的問題就是:客戶端看到了“超時”,實際上操作到底有沒有成功,客戶端并不知道,但通常這也不是什么大問題,因為用戶在瀏覽器上再刷新一下就好了。:D

好,再來看DefferedResult方式的超時處理:

 

  @RequestMapping(value = "/asynctask", method = RequestMethod.GET)
  public DeferredResult<ModelAndView> asyncTask() {
    DeferredResult<ModelAndView> deferredResult = new DeferredResult<ModelAndView>(2000L);
    System.out.println("/asynctask 調(diào)用!thread id is : " + Thread.currentThread().getId());
    longTimeAsyncCallService.makeRemoteCallAndUnknownWhenFinish(new LongTermTaskCallback() {
      @Override
      public void callback(Object result) {
        System.out.println("異步調(diào)用執(zhí)行完成, thread id is : " + Thread.currentThread().getId());
        ModelAndView mav = new ModelAndView("remotecalltask");
        mav.addObject("result", result);
        deferredResult.setResult(mav);
      }
    });

    deferredResult.onTimeout(new Runnable() {
      @Override
      public void run() {
        System.out.println("異步調(diào)用執(zhí)行超時!thread id is : " + Thread.currentThread().getId());
        ModelAndView mav = new ModelAndView("remotecalltask");
        mav.addObject("result", "異步調(diào)用執(zhí)行超時");
        deferredResult.setResult(mav);
      }
    });

    return deferredResult;
  }

非常類似,對吧,我把超時設(shè)置為2秒,而正常處理需要3秒,一定會超時,執(zhí)行結(jié)果如下:

/asynctask 調(diào)用!thread id is : 48

完成此任務(wù)需要 : 3 秒

異步調(diào)用執(zhí)行超時!thread id is : 51

異步調(diào)用執(zhí)行完成, thread id is : 49

完全在我們預(yù)料之中。

異常處理

貌似沒什么差別,在Controller中的處理和之前同步模式的處理是一樣一樣的:

  @ExceptionHandler(Exception.class)
  public ModelAndView handleAllException(Exception ex) {
    ModelAndView model = new ModelAndView("error");
    model.addObject("result", ex.getMessage());
    return model;
  }

還要再弄個全局的異常處理啥的,和過去的做法都一樣,在此不表了。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Springboot異常錯誤處理解決方案詳解

    Springboot異常錯誤處理解決方案詳解

    這篇文章主要介紹了Springboot異常錯誤處理解決方案詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-08-08
  • JavaSE反射、枚舉、lambda表達式常用方法舉例

    JavaSE反射、枚舉、lambda表達式常用方法舉例

    這篇文章主要介紹了JavaSE反射、枚舉、lambda表達式常用方法的相關(guān)資料,反射允許在運行時檢查和操作類、方法和屬性,枚舉提供了一種更安全和優(yōu)雅的方式來定義常量,而Lambda表達式則簡化了函數(shù)式接口的實現(xiàn),需要的朋友可以參考下
    2024-12-12
  • MyBatis-Plus 查詢指定字段的實現(xiàn)

    MyBatis-Plus 查詢指定字段的實現(xiàn)

    這篇文章主要介紹了MyBatis-Plus 查詢指定字段的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • 深入了解SparkSQL的運用及方法

    深入了解SparkSQL的運用及方法

    SparkSQL就是將SQL轉(zhuǎn)換成一個任務(wù),提交到集群上運行,類似于Hive的執(zhí)行方式。本文給大家分享了SparkSQl的運用及方法,感興趣的朋友跟隨小編一起看看吧
    2022-03-03
  • Spring Cloud 請求重試機制核心代碼分析

    Spring Cloud 請求重試機制核心代碼分析

    這篇文章主要介紹了Spring Cloud 請求重試機制核心代碼分析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • Java中實現(xiàn)漢字生成拼音首拼和五筆碼

    Java中實現(xiàn)漢字生成拼音首拼和五筆碼

    這篇文章主要介紹了Java中實現(xiàn)漢字生成拼音首拼和五筆碼方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java常用類庫StringBuffer,Runtime,日期操作類等類庫總結(jié)

    Java常用類庫StringBuffer,Runtime,日期操作類等類庫總結(jié)

    這篇文章主要介紹了Java常用類庫StringBuffer,Runtime,日期操作類等類庫總結(jié),需要的朋友可以參考下
    2020-02-02
  • springboot集成WebSockets廣播消息(推薦)

    springboot集成WebSockets廣播消息(推薦)

    這篇文章主要介紹了springboot-集成WebSockets廣播消息,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-12-12
  • Java實現(xiàn)順時針輸出螺旋二維數(shù)組的方法示例

    Java實現(xiàn)順時針輸出螺旋二維數(shù)組的方法示例

    這篇文章主要介紹了利用Java如何實現(xiàn)順時針輸出螺旋二維數(shù)組的方法示例,文中給出了詳細的示例代碼和注釋,相信對大家具有一定的參考價值,有需要的朋友們下面來一起看看吧。
    2017-02-02
  • MybatisPlus特殊查詢的實現(xiàn)介紹

    MybatisPlus特殊查詢的實現(xiàn)介紹

    這篇文章主要介紹了MybatisPlus查詢投影、聚合查詢、分組查詢、等值查詢、范圍查詢、模糊查詢、排序查詢,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-10-10

最新評論

司法| 修文县| 蕉岭县| 浙江省| 濮阳市| 沙河市| 开封县| 中山市| 伊宁县| 沧州市| 保德县| 罗源县| 汝州市| 东阿县| 永安市| 永新县| 孟津县| 嘉祥县| 舞钢市| 乳源| 沙雅县| 隆子县| 盖州市| 翁牛特旗| 抚远县| 宜兴市| 合江县| 平顶山市| 宜黄县| 台安县| 中宁县| 兰州市| 泸水县| 阿克| 墨竹工卡县| 德保县| 望江县| 舟曲县| 即墨市| 海南省| 霍林郭勒市|