tomcat源码解读五 Tomcat中Request的生命历程

     Request在tomcat中是一个非常核心的的实例,下面以NIO为例来解读一下在各个时期下的状态(其实在Tomcat的几种模式中到了这里之后的处理都是差不多的)

1.1 创建coyote/Request

     这个request并不是我们最终在servlet中使用的Request,它是tomcat内部处理请求的一种有效方法,其创建过程是在接收到客户请求处理套接字构建Processor具体实现类的构造器中构建,以NIO模式为例则是在实例化请求处理类Http11NioProcessor时候构建,具体执行流程如下:

Http11NioProcessor.java

public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint, int maxTrailerSize,
        Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {

    super(endpoint);

    inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize);
    request.setInputBuffer(inputBuffer);

    outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize);
    response.setOutputBuffer(outputBuffer);

    initializeFilters(maxTrailerSize, allowedTrailerHeaders, maxExtensionSize, maxSwallowSize);
}

AbstractHttp11Processor.java

public AbstractHttp11Processor(AbstractEndpoint<S> endpoint) {
    super(endpoint);
    userDataHelper = new UserDataHelper(getLog());
}

AbstractProcessor.java

public AbstractProcessor(AbstractEndpoint<S> endpoint) {
    this.endpoint = endpoint;
    asyncStateMachine = new AsyncStateMachine(this);
    request = new Request();
    response = new Response();
    response.setHook(this);
    request.setResponse(response);
    request.setHook(this);
}

     根据这个过程不难看出在实例化中逐级显示调用父级有参构造器,将对应的endpoint赋给Processor实现类的句柄,而后继续实例化Request,并将当前实例注入到新构建的request实例,另外response也被注入request作为句柄。

1.2 Coyote/Request的执行与结束      Coyote/Request的执行与结束主要是在Processor. process在这个过程中会获取RequestInfo这个句柄其是一个request初始化实例句柄,在这个方法中通过setRequestLineReadTimeout方法解析了请求行如Method URL HTTP/1.1利用parseRequestLine剩余的首部信息最终调用request中相关的方法将解析的信息(大部分是MessageByte)注入到其成员属性中(详见requets解析http头部请求),然后进行的是调用Adapter的service方法进行处理(见下一小节),之后利用endRequest将当前请求处理完成,在这个过程还包含将流给提交http中去,最后调用nextRequest将当前request实例部分属性置空,所以当前实例依旧存在。

public SocketState process(SocketWrapper<S> socketWrapper)
    throws IOException {
    //获取请求信息句柄,其在定义的时候直接初始化的一个RequestInfo实例
    RequestInfo rp = request.getRequestProcessor();
    //设置RequestInfo的状态为解析状态  stage_parse
    rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
    。。。。。。

    while (!getErrorState().isError() && keepAlive && !comet && !isAsync() && upgradeToken == null && !endpoint.isPaused()) {
            。。。。。。   
            //设置请求阅读时间,以及将socketInputStream中的数据写入到buf
            setRequestLineReadTimeout();
            //if判断里只是解析了第一行,方法名 请求URL 协议
            if (!getInputBuffer().parseRequestLine(keptAlive)) {
                //如果解析失败,处理未完成的请
                if (handleIncompleteRequestLineRead()) {
                    break;
                }
。。。。。。。。
        }
        if (!getErrorState().isError()) {
            //设置过滤器准备解析
            rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
            try {
    //解析请求,根据url将对应的Host Context Wrapper匹配到该请request的属性中
                prepareRequest();
            } catch (Throwable t) {
               。。。。。。。。            
}
        }
        //最大的长连接数
        if (maxKeepAliveRequests == 1) {
            keepAlive = false;
        } else if (maxKeepAliveRequests > 0 &&
                socketWrapper.decrementKeepAlive() <= 0) {
            keepAlive = false;
        }
        // 在适配器中执行请求
        if (!getErrorState().isError()) {
                //设置request的状态为开始调用适配器的service
            rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                //调用适配器的service方法
                getAdapter().service(request, response);
          。。。。。。。
        }

           rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);

        if (!isAsync() && !comet) {
            。。。。。。
            endRequest();
        }

        rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);
        if (!isAsync() && !comet || getErrorState().isError()) {
            request.updateCounters();
            if (getErrorState().isIoAllowed()) {
                getInputBuffer().nextRequest();
                getOutputBuffer().nextRequest();
            }
        }

        。。。。。。

        rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
    }

    rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

   }

1.3 创建Coonnector/Request和Connector/Response

     这两个实例和是从相应的Coyote对应实例的Notes 数组中获取的,如果没有则实例化一个并且注入,这是因为Coyote和Coonnector中相关实例是一一对应,只不过Coyote主要是负责和http打交道而Coonnector是和程序员打交道,但是请注意我们并不是直接使用的Coonnector中Request/Response。后面讲述

//从org.apache.coyote.Request的note数组属性中获取Request对象
Request request = (Request) req.getNote(ADAPTER_NOTES);
//从org.apache.coyote.Response的note数组属性中获取Response对象
Response response = (Response) res.getNote(ADAPTER_NOTES);
//解析:ADAPTER_NOTES=1 这是因为notes这个数组不知存放了相应request/Response实例 还有cookie等 1代表的是Request/Response
if (request == null) {
    //创建一个connector的request对象
    request = connector.createRequest();
    //将Coyote中request注入连接器中
    request.setCoyoteRequest(req);
    response = connector.createResponse();
    response.setCoyoteResponse(res);
    //request response相互关联
    request.setResponse(response);
    response.setRequest(request);
    //设置为notes
    req.setNote(ADAPTER_NOTES, request);
    res.setNote(ADAPTER_NOTES, response);
    req.getParameters().setQueryStringEncoding
        (connector.getURIEncoding());

}

if (connector.getXpoweredBy()) {
    response.addHeader("X-Powered-By", POWERED_BY);
}

1.4 Coonnector/Request和Connector/Response的执行过程

     其创建之后就直接通过获取service不断调用管道一下向下执行找到对应的servlet进行执行,其过程如下:

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

     在这个过程中,针对Request/Response的生命历程,我们应该提的是StandardWrapperValve这个阀门执行的代码是如下:      filterChain.doFilter(request.getRequest(),response.getResponse())

public HttpServletRequest getRequest() {
    if (facade == null) {
        facade = new RequestFacade(this);
    }
    return facade;
}

     可以看出在这里采用了外观模式创建了RequestFacade实例,并作为参数不断向下传递,这就是我们在servlet中使用的RequestFacade这个实例在servlet执行完毕,接着管道继续向下执到finishRequest, finishResponse完成当前请求,其中finishResponse是将最终的相应数据给发送到客户端

1.5 Coonnector/Request和Connector/Response的结束处理 Request/Response也不是直接从内存释放,仅仅只是其中部分属性给置空,下一个socket请求的时候调用的是对应的Processor具体实现类则可以直接进行获取。置空代码如下:

if (!comet && !async) {
    request.recycle();
    response.recycle();
} else {
    request.clearEncoders();
    response.clearEncoders();
}

由于Request请求跟浏览器无关,可能多次请求是一个Request实例,也可能是不同实例,但是在请求中Request实例中对应的成员属性都被清空,所以可以说Request的作用域是单个请求,Response也是同样的道理

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java技术栈

深度好文 | Java 可重入锁内存可见性分析

1292
来自专栏Android干货园

Android JS相互调用详解

版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/49...

561
来自专栏码农阿宇

国内开源社区巨作AspectCore-Framework入门

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技...

1201
来自专栏Java帮帮-微信公众号-技术文章全总结

day05.轻量级RPC框架【大数据教程】

day05.轻量级RPC框架【大数据教程】 轻量级RPC框架开发 1. RPC原理学习 1.1. 什么是RPC RPC(Remote Procedure Cal...

3037
来自专栏月色的自留地

macOS webview编程

1754
来自专栏AI星球

Eclipse中使用JUnit4进行单元测试(整合篇)

我们在编写大型程序的时候,需要写成千上万个方法或函数,这些函数的功能可能很强大,但我们在程序中只用到该函数的一小部分功能,并且经过调试可以确定,这一小部分功能是...

532
来自专栏MelonTeam专栏

Viewpager循环滑动的实现

导语 本文讲述实现ViewPager循环滑动效果的两种方案: 方案1: 复写ViewPager或者Adapter,扩展dataList,左右各加1...

1806
来自专栏java思维导图

mybatis-plus思维导图,让mybatis-plus不再难懂

 Mybatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果...

87817
来自专栏技术小讲堂

ASP.NET AJAX(8)__Microsoft AJAX Library中异步通信层的使用什么是异步通信层Micorsoft AJAX Library异步通信层的组成WebRequestExec

什么是异步通信层 Microsoft AJAX Library的组长部分之一 负责ASP.NET AJAX框架中所有的客户端与服务器端的通信 其默认实现了封装了...

3275
来自专栏架构之路

spark 2.3 导致driver OOM的一个SparkPlanGraphWrapper源码的bug

长话短说,我们部门一个同事找到我,说他的spark 2.3 structured streaming程序频繁报OOM,从来没有坚持过超过三四天的,叫帮看一下。 ...

602

扫码关注云+社区