真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

深入淺析Servlet3.0/3.1中的異步處理

深入淺析Servlet 3.0/3.1 中的異步處理?相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

成都創(chuàng)新互聯(lián)專注于企業(yè)營銷型網(wǎng)站建設(shè)、網(wǎng)站重做改版、伊吾網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5高端網(wǎng)站建設(shè)電子商務(wù)商城網(wǎng)站建設(shè)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)營銷網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為伊吾等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。

在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式處理請求,即每一次Http請求都由某一個(gè)線程從頭到尾負(fù)責(zé)處理。如果一個(gè)請求需要進(jìn)行IO操作,比如訪問數(shù)據(jù)庫、調(diào)用第三方服務(wù)接口等,那么其所對應(yīng)的線程將同步地等待IO操作完成, 而IO操作是非常慢的,所以此時(shí)的線程并不能及時(shí)地釋放回線程池以供后續(xù)使用,在并發(fā)量越來越大的情況下,這將帶來嚴(yán)重的性能問題。即便是像Spring、Struts這樣的高層框架也脫離不了這樣的桎梏,因?yàn)樗麄兌际墙⒃赟ervlet之上的。為了解決這樣的問題,Servlet 3.0引入了異步處理,然后在Servlet 3.1中又引入了非阻塞IO來進(jìn)一步增強(qiáng)異步處理的性能。

本文源代碼:https://github.com/davenkin/servlet-3-async-learning

項(xiàng)目下載地址:servlet-3-async-learning_jb51.rar

在Servlet 3.0中,我們可以從HttpServletRequest對象中獲得一個(gè)AsyncContext對象,該對象構(gòu)成了異步處理的上下文,Request和Response對象都可從中獲取。AsyncContext可以從當(dāng)前線程傳給另外的線程,并在新的線程中完成對請求的處理并返回結(jié)果給客戶端,初始線程便可以還回給容器線程池以處理更多的請求。如此,通過將請求從一個(gè)線程傳給另一個(gè)線程處理的過程便構(gòu)成了Servlet 3.0中的異步處理。

舉個(gè)例子,對于一個(gè)需要完成長時(shí)處理的Servlet來說,其實(shí)現(xiàn)通常為:

@WebServlet("/syncHello")
public class SyncHelloServlet extends HttpServlet {

  protected void doGet(HttpServletRequest request,
             HttpServletResponse response) throws ServletException, IOException {
    new LongRunningProcess().run();
    response.getWriter().write("Hello World!");
  }
}

為了模擬長時(shí)處理過程,我們創(chuàng)建了一個(gè)LongRunningProcess類,其run()方法將隨機(jī)地等待2秒之內(nèi)的一個(gè)時(shí)間:

public class LongRunningProcess {

  public void run() {
    try {

      int millis = ThreadLocalRandom.current().nextInt(2000);
      String currentThread = Thread.currentThread().getName();
      System.out.println(currentThread + " sleep for " + millis + " milliseconds.");
      Thread.sleep(millis);

    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

此時(shí)的SyncHelloServlet將順序地先執(zhí)行LongRunningProcess的run()方法,然后將將HelloWorld返回給客戶端,這是一個(gè)典型的同步過程。

在Servlet 3.0中,我們可以這么寫來達(dá)到異步處理:

@WebServlet(value = "/simpleAsync", asyncSupported = true)
public class SimpleAsyncHelloServlet extends HttpServlet {

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    AsyncContext asyncContext = request.startAsync();

    asyncContext.start(() -> {
      new LongRunningProcess().run();
      try {
        asyncContext.getResponse().getWriter().write("Hello World!");
      } catch (IOException e) {
        e.printStackTrace();
      }
      asyncContext.complete();
    });

  }

此時(shí),我們先通過request.startAsync()獲取到該請求對應(yīng)的AsyncContext,然后調(diào)用AsyncContext的start()方法進(jìn)行異步處理,處理完畢后需要調(diào)用complete()方法告知Servlet容器。start()方法會(huì)向Servlet容器另外申請一個(gè)新的線程(可以是從Servlet容器中已有的主線程池獲取,也可以另外維護(hù)一個(gè)線程池,不同容器實(shí)現(xiàn)可能不一樣),然后在這個(gè)新的線程中繼續(xù)處理請求,而原先的線程將被回收到主線程池中。事實(shí)上,這種方式對性能的改進(jìn)不大,因?yàn)槿绻碌木€程和初始線程共享同一個(gè)線程池的話,相當(dāng)于閑置下了一個(gè)線程,但同時(shí)又占用了另一個(gè)線程。

當(dāng)然,除了調(diào)用AsyncContext的start()方法,我們還可以通過手動(dòng)創(chuàng)建線程的方式來實(shí)現(xiàn)異步處理:

@WebServlet(value = "/newThreadAsync", asyncSupported = true)
public class NewThreadAsyncHelloServlet extends HttpServlet {

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    AsyncContext asyncContext = request.startAsync();

    Runnable runnable = () -> {
      new LongRunningProcess().run();
      try {
        asyncContext.getResponse().getWriter().write("Hello World!");
      } catch (IOException e) {
        e.printStackTrace();
      }
      asyncContext.complete();
    };

    new Thread(runnable).start();

  }

}

自己手動(dòng)創(chuàng)建新線程一般是不被鼓勵(lì)的,并且此時(shí)線程不能重用。因此,一種更好的辦法是我們自己維護(hù)一個(gè)線程池。這個(gè)線程池不同于Servlet容器的主線程池,如下圖:

深入淺析Servlet 3.0/3.1 中的異步處理

在上圖中,用戶發(fā)起的請求首先交由Servlet容器主線程池中的線程處理,在該線程中,我們獲取到AsyncContext,然后將其交給異步處理線程池??梢酝ㄟ^Java提供的Executor框架來創(chuàng)建線程池:

@WebServlet(value = "/threadPoolAsync", asyncSupported = true)
public class ThreadPoolAsyncHelloServlet extends HttpServlet {

  private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    AsyncContext asyncContext = request.startAsync();

    executor.execute(() -> {

      new LongRunningProcess().run();

      try {
        asyncContext.getResponse().getWriter().write("Hello World!");
      } catch (IOException e) {
        e.printStackTrace();
      }

      asyncContext.complete();

    });
  }

}

Servlet 3.0對請求的處理雖然是異步的,但是對InputStream和OutputStream的IO操作卻依然是阻塞的,對于數(shù)據(jù)量大的請求體或者返回體,阻塞IO也將導(dǎo)致不必要的等待。因此在Servlet 3.1中引入了非阻塞IO(參考下圖紅框內(nèi)容),通過在HttpServletRequest和HttpServletResponse中分別添加ReadListener和WriterListener方式,只有在IO數(shù)據(jù)滿足一定條件時(shí)(比如數(shù)據(jù)準(zhǔn)備好時(shí)),才進(jìn)行后續(xù)的操作。

深入淺析Servlet 3.0/3.1 中的異步處理

對應(yīng)的代碼示:

@WebServlet(value = "/nonBlockingThreadPoolAsync", asyncSupported = true)
public class NonBlockingAsyncHelloServlet extends HttpServlet {

  private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));

  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    AsyncContext asyncContext = request.startAsync();

    ServletInputStream inputStream = request.getInputStream();

    inputStream.setReadListener(new ReadListener() {
      @Override
      public void onDataAvailable() throws IOException {

      }

      @Override
      public void onAllDataRead() throws IOException {
        executor.execute(() -> {
          new LongRunningProcess().run();

          try {
            asyncContext.getResponse().getWriter().write("Hello World!");
          } catch (IOException e) {
            e.printStackTrace();
          }

          asyncContext.complete();

        });
      }

      @Override
      public void onError(Throwable t) {
        asyncContext.complete();
      }
    });


  }

}

在上例中,我們?yōu)镾ervletInputStream添加了一個(gè)ReadListener,并在ReadListener的onAllDataRead()方法中完成了長時(shí)處理過程。

看完上述內(nèi)容,你們掌握深入淺析Servlet 3.0/3.1 中的異步處理的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!


文章標(biāo)題:深入淺析Servlet3.0/3.1中的異步處理
網(wǎng)址分享:http://weahome.cn/article/gohhhi.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部