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

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

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

服務器之家 - 編程語言 - Java教程 - 淺談servlet3異步原理與實踐

淺談servlet3異步原理與實踐

2021-01-24 11:11新棟BOOK Java教程

本篇文章主要介紹了servlet3異步原理與實踐,詳細的介紹了servlet和異步的流程使用,具有一定的參考價值,有興趣的可以了解一下

一、什么是servlet

servlet 是基于 java 的 web 組件,由容器進行管理,來生成動態內容。像其他基于 java 的組件技術一樣,servlet 也是基于平臺無關的 java 類格式,被編譯為平臺無關的字節碼,可以被基于 java 技術的 web 服務器動態加載并運行。容器(container),有時候也叫做 servlet 引擎,是 web 服務器為支持 servlet 功能擴展的部分。客戶端通過 servlet 容器實現的 request/response paradigm(請求/應答模式) 與 servlet 進行交互。

二、什么是servlet規范

每當一個servlet版本發布都會對應一個servlet版本的規范,比如servlet2.5、servlet3.0、servlet3.1.
規范中描述了java servlet api 的標準,定義了 java servlet api 中類、接口、方法簽名的完整規范且附帶的javadoc 文檔供開發人員查閱,目的主要是為java servlet 給出一個完整和清晰的解釋。從下圖可以看出servlet規范版本和tomcat支持的版本的對應關系。比如servlet3是從tomcat7以后開始支持的。

淺談servlet3異步原理與實踐

servlet和tomcat版本.png

三、同步,異步,阻塞,非阻塞

同步異步是數據通信的方式,阻塞和非阻塞是一種狀態。比如同步這種數據通訊方式里面可以有阻塞狀態也可以有非阻塞狀態。

四、servlet3的異步位置

這里說的位置是指,從tomcat處理整個request請求流程中,異步處于哪一步。我們先梳理出在nio模式下(是否使用nio跟異步沒有直接關系,這里是拿nio模式下的tomcat流程做說明),下面這個圖是tomcat的總體結構,里面用箭頭標明了請求線路。

淺談servlet3異步原理與實踐

tomcat架構圖.png

我們知道在tomcat的組件中connector和engine是最核心的兩個組件,servlet3的異步處理就是發生在connector中。tomcat的組件之間的協作關系,后續會單獨寫一篇文章介紹。這里先有一個直觀的認識。便與后續對異步理解。

五、servlet3的異步流程

淺談servlet3異步原理與實踐

servlet異步處理流程圖.png

接收到request請求之后,由tomcat工作線程從httpservletrequest中獲得一個異步上下文asynccontext對象,然后由tomcat工作線程把asynccontext對象傳遞給業務處理線程,同時tomcat工作線程歸還到工作線程池,這一步就是異步開始。在業務處理線程中完成業務邏輯的處理,生成response返回給客戶端。在servlet3.0中雖然處理請求可以實現異步,但是inputstream和outputstream的io操作還是阻塞的,當數據量大的request body 或者 response body的時候,就會導致不必要的等待。從servlet3.1以后增加了非阻塞io,需要tomcat8.x支持。

六、servlet3的異步使用步驟

我們使用的大致步驟如下:

1、聲明servlet,增加asyncsupported屬性,開啟異步支持。@webservlet(urlpatterns = "/asynclongrunningservlet", asyncsupported = true)
2、通過request獲取異步上下文asynccontext。asynccontext asyncctx = request.startasync();
3、開啟業務邏輯處理線程,并將asynccontext 傳遞給業務線程。executor.execute(new asyncrequestprocessor(asyncctx, secs));
4、在異步業務邏輯處理線程中,通過asynccontext獲取request和response,處理對應的業務。
5、業務邏輯處理線程處理完成邏輯之后,調用asynccontext 的complete方法。asynccontext.complete();從而結束該次異步線程處理。

七、servlet3的異步使用示例

7.1、asynclongrunningservlet.java 處理servlet請求,并開啟異步

?
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
44
45
46
47
48
package com.test.servlet3;
 
import javax.servlet.asynccontext;
import javax.servlet.servletexception;
import javax.servlet.annotation.webservlet;
import javax.servlet.http.httpservlet;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
import java.util.concurrent.threadpoolexecutor;
 
/**
 * created by wangxindong on 2017/10/19.
 */
@webservlet(urlpatterns = "/asynclongrunningservlet", asyncsupported = true)
public class asynclongrunningservlet extends httpservlet {
  private static final long serialversionuid = 1l;
 
  protected void doget(httpservletrequest request,
             httpservletresponse response) throws servletexception, ioexception {
    long starttime = system.currenttimemillis();
    system.out.println("asynclongrunningservlet start::name="
        + thread.currentthread().getname() + "::id="
        + thread.currentthread().getid());
 
    request.setattribute("org.apache.catalina.async_supported", true);
 
    string time = request.getparameter("time");
    int secs = integer.valueof(time);
    // max 10 seconds
    if (secs > 10000)
      secs = 10000;
 
    asynccontext asyncctx = request.startasync();
    asyncctx.addlistener(new appasynclistener());
    asyncctx.settimeout(9000);//異步servlet的超時時間,異步servlet有對應的超時時間,如果在指定的時間內沒有執行完操作,response依然會走原來servlet的結束邏輯,后續的異步操作執行完再寫回的時候,可能會遇到異常。
 
    threadpoolexecutor executor = (threadpoolexecutor) request
        .getservletcontext().getattribute("executor");
 
    executor.execute(new asyncrequestprocessor(asyncctx, secs));
    long endtime = system.currenttimemillis();
    system.out.println("asynclongrunningservlet end::name="
        + thread.currentthread().getname() + "::id="
        + thread.currentthread().getid() + "::time taken="
        + (endtime - starttime) + " ms.");
  }
}

7.2、appasynclistener.java 異步監聽器

?
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
package com.test.servlet3;
 
import javax.servlet.asyncevent;
import javax.servlet.asynclistener;
import javax.servlet.servletresponse;
import javax.servlet.annotation.weblistener;
import java.io.ioexception;
import java.io.printwriter;
 
/**
 * created by wangxindong on 2017/10/19.
 */
@weblistener
public class appasynclistener implements asynclistener {
  @override
  public void oncomplete(asyncevent asyncevent) throws ioexception {
    system.out.println("appasynclistener oncomplete");
    // we can do resource cleanup activity here
  }
 
  @override
  public void onerror(asyncevent asyncevent) throws ioexception {
    system.out.println("appasynclistener onerror");
    //we can return error response to client
  }
 
  @override
  public void onstartasync(asyncevent asyncevent) throws ioexception {
    system.out.println("appasynclistener onstartasync");
    //we can log the event here
  }
 
  @override
  public void ontimeout(asyncevent asyncevent) throws ioexception {
    system.out.println("appasynclistener ontimeout");
    //we can send appropriate response to client
    servletresponse response = asyncevent.getasynccontext().getresponse();
    printwriter out = response.getwriter();
    out.write("timeout error in processing");
  }
}

7.3、appcontextlistener.java servlet上下文監聽器,可以在里面初始化業務線程池

?
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
package com.test.servlet3;
 
import javax.servlet.servletcontextevent;
import javax.servlet.servletcontextlistener;
import javax.servlet.annotation.weblistener;
import java.util.concurrent.arrayblockingqueue;
import java.util.concurrent.threadpoolexecutor;
import java.util.concurrent.timeunit;
 
/**
 * created by wangxindong on 2017/10/19.
 * 在監聽中初始化線程池
 */
@weblistener
public class appcontextlistener implements servletcontextlistener {
  public void contextinitialized(servletcontextevent servletcontextevent) {
 
    // create the thread pool
    threadpoolexecutor executor = new threadpoolexecutor(100, 200, 50000l,
        timeunit.milliseconds, new arrayblockingqueue<runnable>(100));
    servletcontextevent.getservletcontext().setattribute("executor",
        executor);
 
  }
 
  public void contextdestroyed(servletcontextevent servletcontextevent) {
    threadpoolexecutor executor = (threadpoolexecutor) servletcontextevent
        .getservletcontext().getattribute("executor");
    executor.shutdown();
  }
}

7.4、asyncrequestprocessor.java 業務工作線程

?
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
44
45
46
package com.test.servlet3;
 
import javax.servlet.asynccontext;
import java.io.ioexception;
import java.io.printwriter;
 
/**
 * created by wangxindong on 2017/10/19.
 * 業務工作線程
 */
public class asyncrequestprocessor implements runnable {
  private asynccontext asynccontext;
  private int secs;
 
  public asyncrequestprocessor() {
  }
 
  public asyncrequestprocessor(asynccontext asyncctx, int secs) {
    this.asynccontext = asyncctx;
    this.secs = secs;
  }
 
  @override
  public void run() {
    system.out.println("async supported? "
        + asynccontext.getrequest().isasyncsupported());
    longprocessing(secs);
    try {
      printwriter out = asynccontext.getresponse().getwriter();
      out.write("processing done for " + secs + " milliseconds!!");
    } catch (ioexception e) {
      e.printstacktrace();
    }
    //complete the processing
    asynccontext.complete();
  }
 
  private void longprocessing(int secs) {
    // wait for given time before finishing
    try {
      thread.sleep(secs);
    } catch (interruptedexception e) {
      e.printstacktrace();
    }
  }
}

八、tomcat nio connector ,servlet 3.0 async,spring mvc async的關系

對于這幾個概念往往會混淆,這里做一個梳理比較,nio是一種io的模型,對比與傳統的bio,它可以利用較少的線程處理更多的連接從而增加機器的吞吐量,tomcat nio connector是tomcat的一種nio連接模式。異步,前面提到他是一種通訊的方式,它跟nio沒有任務關系,及時沒有nio也可以實現異步,servlet 3.0 async是指servlet 3規范以后支持了異步處理servlet請求,我們可以把請求線程和業務線程分開。spring mvc async是在servlet3異步的基礎上做了一層封裝。具體的區別如下:

8.1、tomcat nio connector

tomcat的connector 有三種模式,bio,nio,apr,tomcat nio connector是其中的nio模式,使得tomcat容器可以用較少的線程處理大量的連接請求,不再是傳統的一請求一線程模式。tomcat的server.xml配置protocol="org.apache.coyote.http11.http11nioprotocol",http11nioprotocol 從 tomcat 6.x 開始支持。nio的細節可以參看nio相關技術文章。

8.2、servlet 3.0 async

是說servlet 3.0支持了業務請求的異步處理,servlet3之前一個請求的處理流程,請求解析、read body,response body,以及其中的業務邏輯處理都由tomcat線程池中的一個線程進行處理的。那么3.0以后我們可以讓請求線程(io線程)和業務處理線程分開,進而對業務進行線程池隔離。我們還可以根據業務重要性進行業務分級,然后再把線程池分級。還可以根據這些分級做其它操作比如監控和降級處理。servlet 3.0 從 tomcat 7.x 開始支持。

8.3、spring mvc async

是spring mvc 3.2 以上版本基于servlet 3的基礎做的封裝,原理及實現方式同上,使用方式如下:

?
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
@controller
@requestmapping("/async/testcontroller")
public class testcontroller {
  @responsebody
  @requestmapping("/{testurl}")
  public deferredresult<responseentity<string>> testprocess(@pathvariable string testurl) {
    final deferredresult<responseentity<string>> deferredresult = new deferredresult<responseentity<string>>();
 
    // 業務邏輯異步處理,將處理結果 set 到 deferredresult
    new thread(new asynctask(deferredresult)).start();
 
    return deferredresult;
  }
 
  private static class asynctask implements runnable {
 
    private deferredresult result;
 
    private asynctask(deferredresult result) {
      this.result = result;
    }
 
    @override
    public void run() {
      //業務邏輯start
      //...
      //業務邏輯end
      result.setresult(result);
    }
  }
}

九、servlet3非阻塞io

servlet3.1以后增加了非阻塞io實現,需要tomcat8.x以上支持。根據servlet3.1規范中的描述”非阻塞 io 僅對在 servlet 中的異步處理請求有效,否則,當調用 servletinputstream.setreadlistener 或servletoutputstream.setwritelistener 方法時將拋出illegalstateexception“??梢哉fservlet3的非阻塞io是對servlet3異步的增強。servlet3的非阻塞是利用java.util.eventlistener的事件驅動機制來實現的。

9.1、asynclongrunningservlet.java 接收請求,獲取讀取請求監聽器readlistener

?
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
package com.test.servlet3noblock;
 
import javax.servlet.asynccontext;
import javax.servlet.servletexception;
import javax.servlet.servletinputstream;
import javax.servlet.annotation.webservlet;
import javax.servlet.http.httpservlet;
import javax.servlet.http.httpservletrequest;
import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
import java.io.printwriter;
 
/**
 * created by wangxindong on 2017/10/23.
 */
@webservlet(urlpatterns = "/asynclongrunningservlet2", asyncsupported = true)
public class asynclongrunningservlet extends httpservlet {
  protected void doget(httpservletrequest request,
             httpservletresponse response) throws servletexception, ioexception {
    request.setcharacterencoding("utf-8");
    response.setcontenttype("text/html;charset=utf-8");
 
    asynccontext actx = request.startasync();//通過request獲得asynccontent對象
 
    actx.settimeout(30*3000);//設置異步調用超時時長
 
    servletinputstream in = request.getinputstream();
    //異步讀取(實現了非阻塞式讀?。?/code>
    in.setreadlistener(new myreadlistener(in,actx));
    //直接輸出到頁面的內容(不等異步完成就直接給頁面)
    printwriter out = response.getwriter();
    out.println("<h1>直接返回頁面,不等異步處理結果了</h1>");
    out.flush();
  }
 
}

9.2、myreadlistener.java 異步處理

?
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
44
45
46
package com.test.servlet3noblock;
 
import javax.servlet.asynccontext;
import javax.servlet.readlistener;
import javax.servlet.servletinputstream;
import java.io.ioexception;
import java.io.printwriter;
 
/**
 * created by wangxindong on 2017/10/23.
 */
public class myreadlistener implements readlistener {
  private servletinputstream inputstream;
  private asynccontext asynccontext;
  public myreadlistener(servletinputstream input,asynccontext context){
    this.inputstream = input;
    this.asynccontext = context;
  }
  //數據可用時觸發執行
  @override
  public void ondataavailable() throws ioexception {
    system.out.println("數據可用時觸發執行");
  }
 
  //數據讀完時觸發調用
  @override
  public void onalldataread() throws ioexception {
    try {
      thread.sleep(3000);//暫停5秒,模擬耗時處理數據
      printwriter out = asynccontext.getresponse().getwriter();
      out.write("數據讀完了");
      out.flush();
      system.out.println("數據讀完了");
    } catch (interruptedexception e) {
      e.printstacktrace();
    }
 
  }
 
  //數據出錯觸發調用
  @override
  public void onerror(throwable t){
    system.out.println("數據 出錯");
    t.printstacktrace();
  }
}

十、總結

通訊模型中的nio可以利用很少的線程處理大量的連接,提高了機器的吞吐量。servlet的異步處理機制使得我們可以將請求異步到獨立的業務線程去執行,使得我們能夠將請求線程和業務線程分離。通訊模型的nio跟servlet3的異步沒有直接關系。但是我們將兩種技術同時使用就更增加了以tomcat為容器的系統的處理能力。自從servlet3.1以后增加了非阻塞的io,這里的非阻塞io是面向inputstream和outputstream流,通過jdk的事件驅動模型來實現,更一步增強了servlet異步的高性能,可以認為是一種增強版的異步機制。

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

原文鏈接:http://www.jianshu.com/p/c23ca9d26f64?utm_source=tuicool&utm_medium=referral

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日本三级免费观看 | 四虎海外影院 | 欧式午夜理伦三级在线观看 | 国产成人福利免费观看 | 午夜视频网站 | 爱爱亚洲 | 美女被吸乳老师羞羞漫画 | 欧式午夜理伦三级在线观看 | 国产成人免费高清激情视频 | 久久成人精品免费播放 | 国产a不卡片精品免费观看 国产aaa伦理片 | 成人小视频在线观看 | japan日韩xxxx69hd japanese在线观看 | 娇喘高潮教室h | 免费港剧在线观看港剧 | 满溢游泳池免费土豪全集下拉版 | 操姓| 国产一区日韩二区欧美三区 | 国产videos hd | 我被黑人彻底征服的全文 | 午夜欧美精品 | 嫩交18xxxx | 草莓视频深夜释放 | 母爱成瘾在线观看 | 国产一级网站 | 久久精品嫩草影院免费看 | 荡女人人爱 | 激情视频亚洲 | 国产成人精品s8sp视频 | 精品网站一区二区三区网站 | 欧美成人免费观看国产 | 男人肌肌捅女人肌肌 | 亚洲乱码一二三四五六区 | 久久不射视频 | 天堂俺去俺来也www久久婷婷 | 久久伊人精品青青草原2021 | 亚洲国产精品嫩草影院久久 | 免费观看一区二区 | 丁香六月色婷婷综合网 | 精品精品国产yyy5857香蕉 | 勾搭已婚高h |