tomcat源码解读五 Tomcat中Request的生命历程
2017-12-02 23:53
267 查看
Request在tomcat中是一个非常核心的的实例,下面以NIO为例来解读一下在各个时期下的状态(其实在Tomcat的几种模式中到了这里之后的处理都是差不多的)
1.1 创建coyote/Request
这个request并不是我们最终在servlet中使用的Request,它是tomcat内部处理请求的一种有效方法,其创建过程是在接收到客户请求处理套接字构建Processor具体实现类的构造器中构建,以NIO模式为例则是在实例化请求处理类Http11NioProcessor时候构建,具体执行流程如下:
![](https://img-blog.csdn.net/20171202234157721?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzI1NDE0MDc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
Http11NioProcessor.java
AbstractHttp11Processor.java
AbstractProcessor.java
根据这个过程不难看出在实例化中逐级显示调用父级有参构造器,将对应的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实例部分属性置空,所以当前实例依旧存在。
1.3 创建Coonnector/Request和Connector/Response
这两个实例和是从相应的Coyote对应实例的Notes 数组中获取的,如果没有则实例化一个并且注入,这是因为Coyote和Coonnector中相关实例是一一对应,只不过Coyote主要是负责和http打交道而Coonnector是和程序员打交道,但是请注意我们并不是直接使用的Coonnector中Request/Response。后面讲述
1.4 Coonnector/Request和Connector/Response的执行过程
其创建之后就直接通过获取service不断调用管道一下向下执行找到对应的servlet进行执行,其过程如下:
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
![](https://img-blog.csdn.net/20171202235051746?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzI1NDE0MDc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
在这个过程中,针对Request/Response的生命历程,我们应该提的是StandardWrapperValve这个阀门执行的代码是如下:
filterChain.doFilter(request.getRequest(),response.getResponse())
可以看出在这里采用了外观模式创建了RequestFacade实例,并作为参数不断向下传递,这就是我们在servlet中使用的RequestFacade这个实例在servlet执行完毕,接着管道继续向下执到finishRequest, finishResponse完成当前请求,其中finishResponse是将最终的相应数据给发送到客户端
1.5 Coonnector/Request和Connector/Response的结束处理
Request/Response也不是直接从内存释放,仅仅只是其中部分属性给置空,下一个socket请求的时候调用的是对应的Processor具体实现类则可以直接进行获取。置空代码如下:
由于Request请求跟浏览器无关,可能多次请求是一个Request实例,也可能是不同实例,但是在请求中Request实例中对应的成员属性都被清空,所以可以说Request的作用域是单个请求,Response也是同样的道理
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也是同样的道理
相关文章推荐
- tomcat源码解读六 tomcat中的session生命历程
- Tomcat源码解读系列——Tomcat的核心组成和启动过程
- 服务器,tomcat,网页请求错误400,The request sent by the client was syntactically incorrect的问题
- 对tomcat来说,每一个进来的请求(request)都需要一个线程,直到该请求结束。
- Tomcat启动过程源码解读
- tomcat学习之request与response
- Tomcat服务器无法运行,出现Bad Request (Invalid Hostname)错误
- nginx+tomcat影响request值
- tomcat源码解读三(2) tomcat中JMX的源码分析
- Tomcat 学习进阶历程之Socket
- 【Tomcat】Invalid character found in the request target
- nginx+tomcat影响request值
- tomcat startup.bat 解读
- 【Tomcat】Tomcat源码解读系列(一)——server.xml文件的配置
- ASP.NET Web 页面生命历程中的一天
- tomcat报错 error parsing HTTP request header Note: further occurrences of HTTP header parsing errors
- application/x-www-form-urlencoded 的contentType,POST数据内容过大,导致tomcat的request取不到参数
- Nginx 与 Tomcat : 413 Request Entity Too Large(请求实体太大)
- iOS-View视图的生命历程
- 对Servlet及tomcat关系的理解,以及Action与servletAPI的关系,以及httpServletRequest