温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Tomcat中异步Servlet的实现原理是什么

发布时间:2021-06-21 18:38:41 来源:亿速云 阅读:364 作者:Leah 栏目:大数据

Tomcat中异步Servlet的实现原理是什么,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

首先,先从使用异步Servlet的方式来说。

我们的使用步骤大致是:

  1. 声明Servlet,注意增加asyncSupported的属性,开启异步处理支持。@WebServlet(urlPatterns = "/demo", asyncSupported = true)

  2. 在Servlet内部,需要独立线程处理的地方,使用request获取异步Context。AsyncContext ctx = req.startAsync();

  3. 在独立线程中,使用异步Context,可以获取到其绑定的request和response,此时,就可以按照原来Servlet的写法,继续编写逻辑了。

  4. 独立线程内的操作处理完成后,需要调用异步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的实现原理是什么的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注亿速云行业资讯频道,感谢各位的阅读!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI