Jetty源码学习7-Continuation

引言

Jetty的NIO可以有效地减少请求的线程数,但是对于servlet而言,依然是每个servlet需要一个线程,Continuation的出现有效地解决了servlet中耗时操作占用请求线程的境况。

Continuation用于处理异步请求,可以在请求处理的上下文交给应用线程去处理并挂起当前请求,这样减少了请求线程的数量,最后当应用线程处理完成后恢复该请求。比如使用该功能可以解决ajax长轮询服务端的境况,异步长连接的功能可见一斑。

Jetty中的Continunation官方文档:http://wiki.eclipse.org/Jetty/Feature/Continuations

Servlet3中规范:http://docs.oracle.com/javaee/6/api/javax/servlet/AsyncContext.html

而Jetty8已经将Continuation集成到了整个框架中,虽然目前并没有很大的应用场景,但是了解该功能亦能无障碍地阅读Jetty的源码。

Continunation结构

1、Continunation的类图

究其名字就是异步长连接的含义,首先了解下其体系结构

Jetty源码学习7-Continuation

蓝色的留作拓展;红色的是恢复请求的基础;黄色的持有Continunation,拥有每个阶段的状态,因此可以有效地控制请求的流转。

2、Continunation的状态

private static final int __IDLE=0;         // Idle request
    private static final int __DISPATCHED=1;   // Request dispatched to filter/servlet
    private static final int __ASYNCSTARTED=2; // Suspend called, but not yet returned to container
    private static final int __REDISPATCHING=3;// resumed while dispatched
    private static final int __ASYNCWAIT=4;    // Suspended and parked
    private static final int __REDISPATCH=5;   // Has been scheduled
    private static final int __REDISPATCHED=6; // Request redispatched to filter/servlet
    private static final int __COMPLETING=7;   // complete while dispatched
    private static final int __UNCOMPLETED=8;  // Request is completable
    private static final int __COMPLETED=9;    // Request is complete

Jetty源码学习7-Continuation

图中不同颜色对应这请求不同的流程,理解该图对于本节的总结至关重要。

1)蓝色的表示正常请求流程

2)红色的表示请求被挂起,并交出线程

3)橙色的表示请求先是被挂起,交出线程前,又被resume恢复了,因此会重复被server去handle。

4)黑色的表示被挂起的请求在调用resume或者超时的情况下被重新恢复。

5)绿色有两层涵义:线程挂起后交出线程前和交出线程之后。可以看到,无论哪种情况走的流程都是一样的。但是区别于resume,complete正如其含义:“完成,结束”,请求是不会再次被递交给server去执行的,因此只适用于交出线程前需要提前结束的时候调用(交出线程之后就算超时了被再次调用也不会被server去执行,因此没有意义)

6)为了简要,图中状态图并不全,但是对于理解Continunation已经足够了。

Jetty中的Continunation

对于Jetty中Continunation的理解其实只要理解到挂起的Request如何管理和Continunation在Jetty请求的处理流程中的地位即可。

1、AbstractHttpConnection

protected void handleRequest() throws IOException
    {
        boolean error = false;

        String threadName=null;
        Throwable async_exception=null;
        try
        {
            if (LOG.isDebugEnabled())
            {
                threadName=Thread.currentThread().getName();
                Thread.currentThread().setName(threadName+" - "+_uri);
            }
            final Server server=_server;
            //标志服务器处理请求了,修改状态位
            boolean handling=_request._async.handling() && server!=null && server.isRunning();
            while (handling)
            {
                _request.setHandled(false);

                String info=null;
                try
                {
                    _uri.getPort();
                    String path = null;

                    try
                    {
                        path = _uri.getDecodedPath();
                    }
                    catch (Exception e)
                    {
                        LOG.warn("Failed UTF-8 decode for request path, trying ISO-8859-1");
                        LOG.ignore(e);
                        path = _uri.getDecodedPath(StringUtil.__ISO_8859_1);
                    }

                    info=URIUtil.canonicalPath(path);
                    if (info==null && !_request.getMethod().equals(HttpMethods.CONNECT))
                    {
                        if (_uri.getScheme()!=null && _uri.getHost()!=null)
                            info="/";
                        else
                            throw new HttpException(400);
                    }
                    _request.setPathInfo(info);

                    if (_out!=null)
                        _out.reopen();

                    if (_request._async.isInitial())
                    {
                        _request.setDispatcherType(DispatcherType.REQUEST);
                        _connector.customize(_endp, _request);
                        //应用的入口
                        server.handle(this);
                    }
                    else
                    {
                        _request.setDispatcherType(DispatcherType.ASYNC);
                        server.handleAsync(this);
                    }
                }
                catch (ContinuationThrowable e)
                {
                    LOG.ignore(e);
                }
                catch (EofException e)
                {
                    async_exception=e;
                    LOG.debug(e);
                    error=true;
                    _request.setHandled(true);
                    if (!_response.isCommitted())
                        _generator.sendError(500, null, null, true);
                }
                catch (RuntimeIOException e)
                {
                    async_exception=e;
                    LOG.debug(e);
                    error=true;
                    _request.setHandled(true);
                }
                catch (HttpException e)
                {
                    LOG.debug(e);
                    error=true;
                    _request.setHandled(true);
                    _response.sendError(e.getStatus(), e.getReason());
                }
                catch (Throwable e)
                {
                    async_exception=e;
                    LOG.warn(String.valueOf(_uri),e);
                    error=true;
                    _request.setHandled(true);
                    _generator.sendError(info==null?400:500, null, null, true);
                }
                finally
                {
                    //标志服务器已经处理完请求了,修改状态位并判断是不是需要再次被Server处理一次,主要适用于一次请求线程暂时被挂起又被resume了。
                    handling = !_request._async.unhandle() && server.isRunning() && _server!=null;
                }
            }
        }
        finally
        {
            if (threadName!=null)
                Thread.currentThread().setName(threadName);

            if (_request._async.isUncompleted())
            {
                //请求处理的最后一步,主要是记录一些错误信息
                _request._async.doComplete(async_exception);

                if (_expect100Continue)
                {
                    LOG.debug("100 continues not sent");                  
                    _expect100Continue = false;
                    if (!_response.isCommitted())
                        _generator.setPersistent(false);
                }

                if(_endp.isOpen())
                {
                    if (error)
                    {
                        _endp.shutdownOutput();
                        _generator.setPersistent(false);
                        if (!_generator.isComplete())
                            _response.complete();
                    }
                    else
                    {
                        if (!_response.isCommitted() && !_request.isHandled())
                            _response.sendError(HttpServletResponse.SC_NOT_FOUND);
                        _response.complete();
                        if (_generator.isPersistent())
                            _connector.persist(_endp);
                    }
                }
                else
                {
                    _response.complete();
                }

                _request.setHandled(true);
            }
        }
    }

2、在请求处理完了之后服务器会判断是不是有被挂起的标记,即ASYNCSTARTED,如果有,则加入超时队列中。

protected boolean unhandle()
    {
        synchronized (this)
        {
            switch(_state)
            {             
                case __ASYNCSTARTED:
                    _initial=false;
                    _state=__ASYNCWAIT;
                    scheduleTimeout(); // could block and change state.
                    if (_state==__ASYNCWAIT)
                        return true;
                    else if (_state==__COMPLETING)
                    {
                        _state=__UNCOMPLETED;
                        return true;
                    }         
                    _initial=false;
                    _state=__REDISPATCHED;
                    return false;           
            }
        }
    }

随后注册到SelectSet的TimeOut队列中,在每次doSelect的时候被检查。

Jetty源码学习7-Continuation

SelectSet在进行doSelect时的检查逻辑:

now=System.currentTimeMillis();
                _timeout.setNow(now);
                Task task = _timeout.expired();
                while (task!=null)
                {
                    if (task instanceof Runnable)
                        dispatch((Runnable)task);
                    task = _timeout.expired();
                }

你可能感兴趣的:(异步,jetty,continuation,长连接,servlet3)