Github地址html
相關係列文章:java
Servlet 3.0 開始提供了AsyncContext用來支持異步處理請求,那麼異步處理請求到底可以帶來哪些好處?git
Web容器通常來講處理請求的方式是:爲每一個request分配一個thread。咱們都知道thread的建立不是沒有代價的,Web容器的thread pool都是有上限的。
那麼一個很容易預見的問題就是,在高負載狀況下,thread pool都被佔着了,那麼後續的request就只能等待,若是運氣很差客戶端會報等待超時的錯誤。
在AsyncContext出現以前,解決這個問題的惟一辦法就是擴充Web容器的thread pool。github
可是這樣依然有一個問題,考慮如下場景:web
有一個web容器,線程池大小200。有一個web app,它有兩個servlet,Servlet-A處理單個請求的時間是10s,Servlet-B處理單個請求的時間是1s。
如今遇到了高負載,有超過200個request到Servlet-A,若是這個時候請求Servlet-B就會等待,由於全部HTTP thread都已經被Servlet-A佔用了。
這個時候工程師發現了問題,擴展了線程池大小到400,可是負載依然持續走高,如今有400個request到Servlet-A,Servlet-B依然沒法響應。segmentfault
看到問題了沒有,由於HTTP thread和Worker thread耦合在了一塊兒(就是同一個thread),因此致使了當大量request到一個耗時操做時,就會將HTTP thread佔滿,致使整個Web容器就會沒法響應。oracle
可是若是使用AsyncContext,咱們就能夠將耗時的操做交給另外一個thread去作,這樣HTTP thread就被釋放出來了,能夠去處理其餘請求了。app
注意,只有使用AsyncContext纔可以達到上面所講的效果,若是直接new Thread()或者相似的方式的,HTTP thread並不會歸還到容器。
下面是一個官方的例子:異步
@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true) public class AsyncServlet extends HttpServlet { /* ... Same variables and init method as in SyncServlet ... */ @Override public void doGet(HttpServletRequest request, HttpServletResponse response) { response.setContentType("text/html;charset=UTF-8"); final AsyncContext acontext = request.startAsync(); acontext.start(new Runnable() { public void run() { String param = acontext.getRequest().getParameter("param"); String result = resource.process(param); HttpServletResponse response = acontext.getResponse(); /* ... print to the response ... */ acontext.complete(); } }); } }
在這個官方例子裏,每一個HTTP thread都會開啓另外一個Worker thread來處理請求,而後把HTTP thread就歸還給Web容器。可是看AsyncContext.start()
方法的javadoc:async
Causes the container to dispatch a thread, possibly from a managed thread pool, to run the specified Runnable.
實際上這裏並無規定Worker thread到底從哪裏來,也許是HTTP thread pool以外的另外一個thread pool?仍是說就是HTTP thread pool?
The Limited Usefulness of AsyncContext.start()文章裏寫道:不一樣的Web容器對此有不一樣的實現,不過Tomcat其實是利用HTTP thread pool來處理AsyncContext.start()
的。
這也就是說,咱們本來是想釋放HTTP thread的,但實際上並無,由於有HTTP thread依然被用做Worker thread,只不過這個thread和接收請求的HTTP thread不是同一個而已。
這個結論咱們也能夠經過AsyncServlet1和SyncServlet的Jmeter benchmark看出來,二者的throughput結果差很少。啓動方法:啓動Main,而後利用Jmeter啓動benchmark.jmx(Tomcat默認配置下HTTP thread pool=200)。
前面看到了Tomcat並無單獨維護Worker thread pool,那麼咱們就得本身想辦法搞一個,見AsyncServlet2,它使用了一個帶Thread pool的ExecutorService來處理AsyncContext。
因此對於AsyncContext的使用並無固定的方式,你能夠根據實際須要去採用不一樣的方式來處理,爲此你須要一點Java concurrent programming的知識。
AsyncContext的目的並非爲了提升性能,也並不直接提供性能提高,它提供了把HTTP thread和Worker thread解藕的機制,從而提升Web容器的響應能力。
不過AsyncContext在某些時候的確可以提升性能,但這個取決於你的代碼是怎麼寫的。
好比:Web容器的HTTP thread pool數量200,某個Servlet使用一個300的Worker thread pool來處理AsyncContext。
相比Sync方式Worker thread pool=HTTP thread pool=200,在這種狀況下咱們有了300的Worker thread pool,因此確定可以帶來一些性能上的提高(畢竟幹活的人多了)。
相反,若是當Worker thread的數量<=HTTP thread數量的時候,那麼就不會獲得性能提高,由於此時處理請求的瓶頸在Worker thread。
你能夠修改AsyncServlet2的線程池大小,把它和SyncServlet比較benchmark結果來驗證這一結論。
必定不要認爲Worker thread pool必須比HTTP thread pool大,理由以下:
因此在更多時候,Worker thread pool不會很大,並且會根據不一樣業務構建不一樣的Worker thread pool。
好比:Web容器thread pool大小200,一個慢速Servlet的Worker thread pool大小10,這樣一來,不管有多少請求到慢速操做,它都不會將HTTP thread佔滿致使其餘請求沒法處理。