天天看點

異步servlet的原理探究

異步servlet是servlet3.0開始支援的,對于單次通路來講,同步的servlet相比異步的servlet在響應時長上并不會帶來變化(這也是常見的誤區之一),但對于高并發的服務而言異步servlet能增加服務端的吞吐量。本篇來從源碼角度上來探究為何說異步servlet能增加服務端的吞吐量的?

首先來個簡單的異步servlet的demo

上面的代碼寫異步servlet的寫法最關鍵的就是

AsyncContext ctx = req.startAsync()

ctx.complete()

我們先講講下當邏輯進入servlet之前,tomcat經曆了哪些步驟:

tcp三次握手後

Acceptor線程處理 socket accept

Acceptor線程處理 注冊registered OP_READ到多路複用器

ClientPoller線程 監聽多路複用器的事件(OP_READ)觸發

從tomcat的work線程池取一個工作線程來處理socket[http-nio-8080-exec-xx],下面幾個步驟也都是在work線程中進行處理的

因為是http協定是以用Http11Processor來解析協定

CoyoteAdapter來适配包裝成Request和Response對象

開始走pipeline管道(Valve),最後一個invoke的是把我們的servlet對象包裝的StandardWrapperValve管道

接下來就走到我們的servlet,由于是我們是異步的servlet,

從這裡開始有2個線程我們要特别關注它們分别做了哪些事情:

tomcat的work線程

我們自定義的業務線程

當在tomcat的work線程中調用startAsync(),會建立了一個異步的上下文(AsyncContext),并且異步的上下文(AsyncContext)會設定這個狀态機狀态為 STARTING, 然後把這個異步上下文放到了我們的自定義線程池中去執行,

對于異步的servlet,有一個專門的狀态機來控制:AsyncMachine,如下圖

異步servlet的原理探究

image

那狀态機的扭轉控制肯定也做針對異步做了什麼特殊處理

異步servlet的原理探究

這裡是一個Socket狀态的切換的處理邏輯,在異步servlet的時候是通過AsyncMachined的狀态來連動Socket狀态

如上圖異步狀态機的切換過程為:

DISPATCHED(初始)->STARTING->STARTED->COMPLETING

Socket的狀态的切換為:LONG

對于tomcat的work線程而言,servlet調用就結束了! 正常來說,如果是同步servlet的話,request和response會在servlet執行完成後由tomcat釋放掉!

異步servlet的原理探究

異步的話 在這個時機request和response肯定不能釋放掉,釋放那不就沒得完了!

雖然Request和Response沒有釋放,但是這根work線程回到tomcat的線程池中去了(非核心線程的話那就釋放)。

回到我們的業務線程,處理完業務邏輯後,調用ctx.complete()

注意:COMPLETING是在我們的自定義的業務線程改變的!

修改狀态會觸發 新開一個tomcat工作線程

異步servlet的原理探究

異步狀态狀态切換:

COMPLETING->DISPATCHED

Socket狀态切換為ASYNC_END

如下圖,一次異步的完整過程如下圖:

異步servlet的原理探究

研究了整個如何異步的過程,雖然這個狀态機的切換挺繞的,會發現在異步servlet中,最大的改變是為了盡快的釋放tomcat的work線程,讓它有機會請求新accept過來的請求,接受更多的請求,當在自定義線程池中處理好業務邏輯後,在去啟動新的tomcat的work線程來處理response,這樣不就很好了解了為什麼說異步servlet能增加服務端的吞吐量了對吧!

思考:

 SpringBoot的@EnableAsync背後是異步servlet嗎?​

 servlet 3.1的non-blocking I/O 解決了3.0的什麼問題?

關注公衆号一起學習

異步servlet的原理探究

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”将是我最大的寫作動力!歡迎各位轉載,轉載文章之後須在文章頁面明顯位置給出作者和原文連接配接,謝謝。