您好,登錄后才能下訂單哦!
Tomcat中異步Servlet的實(shí)現(xiàn)原理是什么,相信很多沒有經(jīng)驗(yàn)的人對(duì)此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。
首先,先從使用異步Servlet的方式來說。
我們的使用步驟大致是:
聲明Servlet,注意增加asyncSupported的屬性,開啟異步處理支持。@WebServlet(urlPatterns = "/demo", asyncSupported = true)
在Servlet內(nèi)部,需要獨(dú)立線程處理的地方,使用request獲取異步Context。AsyncContext ctx = req.startAsync();
在獨(dú)立線程中,使用異步Context,可以獲取到其綁定的request和response,此時(shí),就可以按照原來Servlet的寫法,繼續(xù)編寫邏輯了。
獨(dú)立線程內(nèi)的操作處理完成后,需要調(diào)用異步Context的complet方法,來結(jié)束該異步線程。
需要注意的是,異步Servlet有對(duì)應(yīng)的超時(shí)時(shí)間,如果在指定的時(shí)間內(nèi)沒有執(zhí)行完操作,response依然會(huì)走原來Servlet的結(jié)束邏輯,后續(xù)的異步操作執(zhí)行完再寫回的時(shí)候,可能會(huì)遇到異常。
上面是使用異步Servlet的幾個(gè)主要步驟,那這個(gè)背后,是如何實(shí)現(xiàn)的呢?下面我們來看原理。
我們?cè)谕ㄟ^Request來獲取異步的Context,這一步背后發(fā)生了什么呢?
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,就是前面步驟里提到的,配置注解的時(shí)候,要開啟異步處理支持,否則在這一步直接就會(huì)拋異常。
注意2,就是前面提到的異步超時(shí)設(shè)置。
我們看到整個(gè)方法是通過當(dāng)前的request和response,生成了一個(gè)對(duì)應(yīng)的AsyncContext,并進(jìn)行相應(yīng)的配置。
在setStart方法中,執(zhí)行的主要邏輯有以下幾點(diǎn):
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內(nèi)部,許多的響應(yīng)狀態(tài)通知,都是通過類似的方式,以ActionCode的方式返回的,包括前面說的complete的方法調(diào)用等。
注意4,是關(guān)于異步的Context,可以添加許多的異步Listener,在特定的事件發(fā)生時(shí),通知到Listener。
在Servlet規(guī)范中,對(duì)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的實(shí)現(xiàn)原理,我們從源碼中來梳理具體的邏輯。
我們前面的文章分析過,整個(gè)請(qǐng)求的處理,到Container內(nèi)各組件的調(diào)用,是從EndPoint到CoyoteAdaptor。其中,在Endpoint的代碼中,調(diào)用是從這一行開始:
getAdapter().service(request, response);
而在此之后,整個(gè)service執(zhí)行完成時(shí),Adaptor中,會(huì)根據(jù)請(qǐng)求類型判斷,對(duì)于非異步和comet的請(qǐng)求,會(huì)進(jìn)行request和response的finished的操作,同時(shí)會(huì)進(jìn)行recycle的操作。
AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
if (asyncConImpl != null) {
async = true;
} else if (!comet) {
request.finishRequest();
response.finishResponse();}
注意,在finishResponse的時(shí)候,outputBuffer.close();就會(huì)執(zhí)行,recycle的時(shí)候,也是根據(jù)請(qǐng)求類型進(jìn)行限制。注意CoyoteAdaptor中,會(huì)判斷具體的請(qǐng)求種類,是否為comet或者async,
if (!comet && !async || error.get()) {
request.recycle();
response.recycle();
此時(shí),OutputBuffer就會(huì)被重置,所對(duì)于普通Servlet,以后面的其它操作就不能被繼續(xù)寫回了。
此外,Processor會(huì)判斷請(qǐng)求的類型,從而決定是否進(jìn)行特定的操作,比如
if (!isAsync() && !comet) {
endRequest();
}
我們看到,在非異步請(qǐng)求并且也不是comet請(qǐng)求時(shí),會(huì)執(zhí)行endRequest操作。而具體返回Socket狀態(tài)的時(shí)候,也是根據(jù)請(qǐng)求類型進(jìn)行判斷,對(duì)于異步的請(qǐng)求,返回的Socket狀態(tài)為LONG,
else if (isAsync() || comet) {
return SocketState.LONG;
Protocol類中,判斷類型為L(zhǎng)ONG時(shí),會(huì)進(jìn)行如下操作:
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的屬性
從而請(qǐng)求可以使用原處理器進(jìn)行處理。
至于異步Context執(zhí)行完成后的complete操作,主要是返回一個(gè)complete的ActionCode,在Processor中據(jù)此判斷請(qǐng)求執(zhí)行完成,設(shè)置SocketStatus為OPEN_READ,開始下一輪請(qǐng)求的接收。
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的基本實(shí)現(xiàn)原理??偨Y(jié)起來主要有:
主線請(qǐng)求執(zhí)行完后Socket的longPool
response的狀態(tài)未設(shè)置為finished,此時(shí)OutputBuffer未close,依然可以寫回。
看完上述內(nèi)容,你們掌握Tomcat中異步Servlet的實(shí)現(xiàn)原理是什么的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。