一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 詳解Servlet 3.0/3.1 中的異步處理

詳解Servlet 3.0/3.1 中的異步處理

2020-09-07 09:02無知者云 Java教程

這篇文章主要介紹了詳解Servlet 3.0/3.1 中的異步處理,實例分析了servlet 3.0異步處理的技巧,非常具有實用價值,需要的朋友可以參考下

Servlet 3.0之前,Servlet采用Thread-Per-Request的方式處理請求,即每一次Http請求都由某一個線程從頭到尾負責處理。如果一個請求需要進行IO操作,比如訪問數據庫、調用第三方服務接口等,那么其所對應的線程將同步地等待IO操作完成, 而IO操作是非常慢的,所以此時的線程并不能及時地釋放回線程池以供后續使用,在并發量越來越大的情況下,這將帶來嚴重的性能問題。即便是像Spring、Struts這樣的高層框架也脫離不了這樣的桎梏,因為他們都是建立在Servlet之上的。為了解決這樣的問題,Servlet 3.0引入了異步處理,然后在Servlet 3.1中又引入了非阻塞IO來進一步增強異步處理的性能。

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

項目下載地址:servlet-3-async-learning.rar

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

舉個例子,對于一個需要完成長時處理的Servlet來說,其實現通常為:

?
1
2
3
4
5
6
7
8
9
@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!");
  }
}

為了模擬長時處理過程,我們創建了一個LongRunningProcess類,其run()方法將隨機地等待2秒之內的一個時間:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();
    }
  }
}

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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();
    });
 
  }

此時,我們先通過request.startAsync()獲取到該請求對應的AsyncContext,然后調用AsyncContext的start()方法進行異步處理,處理完畢后需要調用complete()方法告知Servlet容器。start()方法會向Servlet容器另外申請一個新的線程(可以是從Servlet容器中已有的主線程池獲取,也可以另外維護一個線程池,不同容器實現可能不一樣),然后在這個新的線程中繼續處理請求,而原先的線程將被回收到主線程池中。事實上,這種方式對性能的改進不大,因為如果新的線程和初始線程共享同一個線程池的話,相當于閑置下了一個線程,但同時又占用了另一個線程。

當然,除了調用AsyncContext的start()方法,我們還可以通過手動創建線程的方式來實現異步處理:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@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();
 
  }
 
}

自己手動創建新線程一般是不被鼓勵的,并且此時線程不能重用。因此,一種更好的辦法是我們自己維護一個線程池。這個線程池不同于Servlet容器的主線程池,如下圖:

詳解Servlet 3.0/3.1 中的異步處理

在上圖中,用戶發起的請求首先交由Servlet容器主線程池中的線程處理,在該線程中,我們獲取到AsyncContext,然后將其交給異步處理線程池。可以通過Java提供的Executor框架來創建線程池:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@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操作卻依然是阻塞的,對于數據量大的請求體或者返回體,阻塞IO也將導致不必要的等待。因此在Servlet 3.1中引入了非阻塞IO(參考下圖紅框內容),通過在HttpServletRequest和HttpServletResponse中分別添加ReadListener和WriterListener方式,只有在IO數據滿足一定條件時(比如數據準備好時),才進行后續的操作。

詳解Servlet 3.0/3.1 中的異步處理

對應的代碼示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@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();
      }
    });
 
 
  }
 
}

在上例中,我們為ServletInputStream添加了一個ReadListener,并在ReadListener的onAllDataRead()方法中完成了長時處理過程。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:http://www.jianshu.com/p/05a57d00d5cb

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产一级特黄aa大片免费 | 王晶三级作品 | 欧美同志网址 | 4虎影视国产在线观看精品 4s4s4s4s色大众影视 | 猫咪社区免费资源在线观看 | 久久精品中文騷妇女内射 | 精品综合久久久久久97超人 | 免费的强动漫人物的 | 日韩不卡一区二区三区 | 色天使亚洲综合在线观看 | 精品欧美一区二区三区在线观看 | 久久久WWW免费人成精品 | 思思91精品国产综合在线 | 国产精品成人一区二区1 | 青青热久免费精品视频网站 | poren黑人| 亚洲欧美日韩天堂 | 高清一区 | 美女张开腿让男人桶的 视频 | 黄片毛片 | 惩罚狠h调教灌满 | 亚洲第一色区 | 新影音先锋男人色资源网 | 成人在线观看网站 | 久久视频这有精品63在线国产 | 婷婷99视频精品全部在线观看 | 国产伦精品一区二区三区女 | s0e一923春菜花在线播放 | 欧美一区二区三区不卡视频 | 日本视频中文字幕 | 91传媒在线观看 | 免费观看成年人视频 | 免费免费啪视频在线观播放 | 无人区1免费完整观看 | 国产日韩欧美综合一区二区三区 | 羞羞污视频 | 97午夜视频| 日韩一级片在线播放 | 亚洲精品福利在线 | 大伊香蕉精品二区视频在线 | 美女光屁股网站 |