您好,登錄后才能下訂單哦!
Tomcat中異步Servlet的實現原理是什么,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
首先,先從使用異步Servlet的方式來說。
我們的使用步驟大致是:
聲明Servlet,注意增加asyncSupported的屬性,開啟異步處理支持。@WebServlet(urlPatterns = "/demo", asyncSupported = true)
在Servlet內部,需要獨立線程處理的地方,使用request獲取異步Context。AsyncContext ctx = req.startAsync();
在獨立線程中,使用異步Context,可以獲取到其綁定的request和response,此時,就可以按照原來Servlet的寫法,繼續編寫邏輯了。
獨立線程內的操作處理完成后,需要調用異步Context的complet方法,來結束該異步線程。
需要注意的是,異步Servlet有對應的超時時間,如果在指定的時間內沒有執行完操作,response依然會走原來Servlet的結束邏輯,后續的異步操作執行完再寫回的時候,可能會遇到異常。
上面是使用異步Servlet的幾個主要步驟,那這個背后,是如何實現的呢?下面我們來看原理。
我們在通過Request來獲取異步的Context,這一步背后發生了什么呢?
public AsyncContext startAsync() {
return startAsync(getRequest(),response.getResponse());
}
public AsyncContext startAsync(ServletRequest request,
ServletResponse response) {
if (!isAsyncSupported()) { //注意1
throw new IllegalStateException(sm.getString("request.asyncNotSupported"));
}
if (asyncContext == null) {
asyncContext = new AsyncContextImpl(this);
}
asyncContext.setStarted(getContext(), request, response,
request==getRequest() && response==getResponse().getResponse());
asyncContext.setTimeout(getConnector().getAsyncTimeout());//注意2
return asyncContext;
}
我們上面的注意1,就是前面步驟里提到的,配置注解的時候,要開啟異步處理支持,否則在這一步直接就會拋異常。
注意2,就是前面提到的異步超時設置。
我們看到整個方法是通過當前的request和response,生成了一個對應的AsyncContext,并進行相應的配置。
在setStart方法中,執行的主要邏輯有以下幾點:
public void setStarted(Context context, ServletRequest request,
ServletResponse response, boolean originalRequestResponse) {
this.request.getCoyoteRequest().action(
ActionCode.ASYNC_START, this); //注意3
List<AsyncListenerWrapper> listenersCopy = new ArrayList<>();
listenersCopy.addAll(listeners);
listeners.clear();
for (AsyncListenerWrapper listener : listenersCopy) {
try {
listener.fireOnStartAsync(event); // 注意4
} catch (Throwable t) {
}
}
}
上面注意3的地方,在Tomcat內部,許多的響應狀態通知,都是通過類似的方式,以ActionCode的方式返回的,包括前面說的complete的方法調用等。
注意4,是關于異步的Context,可以添加許多的異步Listener,在特定的事件發生時,通知到Listener。
在Servlet規范中,對startAsync方法做了這樣的描述:
A call to this method ensures that the response isn't committed when the application exits out of the service method. It is committed when AsyncContext.complete is called on the returned AsyncContext or the AsyncContext times out and there are no listeners associated to handle the time out. The timer for async time outs will not start until the request and it’s associated response have returned from the container. The AsyncContext could be used to write to the response from the async thread. It can also be used to just notify that the response is not closed and committed.
以上說明,可以幫助我們理解一部分異步Servlet的實現原理,我們從源碼中來梳理具體的邏輯。
我們前面的文章分析過,整個請求的處理,到Container內各組件的調用,是從EndPoint到CoyoteAdaptor。其中,在Endpoint的代碼中,調用是從這一行開始:
getAdapter().service(request, response);
而在此之后,整個service執行完成時,Adaptor中,會根據請求類型判斷,對于非異步和comet的請求,會進行request和response的finished的操作,同時會進行recycle的操作。
AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
if (asyncConImpl != null) {
async = true;
} else if (!comet) {
request.finishRequest();
response.finishResponse();}
注意,在finishResponse的時候,outputBuffer.close();就會執行,recycle的時候,也是根據請求類型進行限制。注意CoyoteAdaptor中,會判斷具體的請求種類,是否為comet或者async,
if (!comet && !async || error.get()) {
request.recycle();
response.recycle();
此時,OutputBuffer就會被重置,所對于普通Servlet,以后面的其它操作就不能被繼續寫回了。
此外,Processor會判斷請求的類型,從而決定是否進行特定的操作,比如
if (!isAsync() && !comet) {
endRequest();
}
我們看到,在非異步請求并且也不是comet請求時,會執行endRequest操作。而具體返回Socket狀態的時候,也是根據請求類型進行判斷,對于異步的請求,返回的Socket狀態為LONG,
else if (isAsync() || comet) {
return SocketState.LONG;
Protocol類中,判斷類型為LONG時,會進行如下操作:
if (state == SocketState.LONG) {
// In the middle of processing a request/response. Keep the
// socket associated with the processor. Exact requirements
// depend on type of long poll
connections.put(socket, processor);
longPoll(wrapper, processor);// 配置processor的屬性
從而請求可以使用原處理器進行處理。
至于異步Context執行完成后的complete操作,主要是返回一個complete的ActionCode,在Processor中據此判斷請求執行完成,設置SocketStatus為OPEN_READ,開始下一輪請求的接收。
public void complete() {
check();
request.getCoyoteRequest().action(ActionCode.ASYNC_COMPLETE, null);
}
case ASYNC_COMPLETE: {
socketWrapper.clearDispatches();
if (asyncStateMachine.asyncComplete()) {
endpoint.processSocket(this.socketWrapper, SocketStatus.OPEN_READ, true);
}
break;
}
以上,即為異步Servlet的基本實現原理。總結起來主要有:
主線請求執行完后Socket的longPool
response的狀態未設置為finished,此時OutputBuffer未close,依然可以寫回。
看完上述內容,你們掌握Tomcat中異步Servlet的實現原理是什么的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。