Tomcat中的JioEndpoint负责socket的接口绑定,JioEndpoint中的Acceptor负责接收socket请求,当接收到一个socket请求后,将执行如下流程:
JioEndpoint.proccessSocket方法
该方法主要完成如下步骤:
- 将socket进行包装,包装为SocketWrapper;
- 将SocketWrapper包装为SocketProcessor,SocketProcessor实现了Runnable
- 将SocketProcessor扔进线程池,等待线程池的调度
SocketProcessor执行流程
SocketProcessor是一个线程对象,在run方法中主要完成以下两件事情:
- serverSocketFactory.handshake(socket.getSocket()):进行ssl的握手
- handler.process(socket, SocketStatus.OPEN_READ):使用handler处理socket,这里的handler就是Http11Protocol类的内部类Http11ConnectionHandler。
所以,真正处理socket的逻辑转移到了Http11ConnectionHandler中。
Http11ConnectionHandler
Http11ConnectionHandler继承了AbstractConnectionHandler类,process方法在AbstractConnectionHandler中。
AbstractConnectionHandler中的process方法执行流程:
- Socket socket = wrapper.getSocket():从SocketWrapper中获取被包装的原始Socket
- Processor processor = createProcessor(); 创建一个Processor,具体创建的逻辑在Http11ConnectionHandler类中实现
- state = processor.process(wrapper):使用Processor处理socketWrapper
所以,真正处理socket的逻辑转移到了Processor中。
Http11Processor
Http11ConnectionHandler的createProcessor方法会创建出来一个Http11Processor对象。在创建Http11Processor时,会把Http11Protocol中很多属性同步到Http11Processor属性中,同时还会初始化Http11Processor中还两个非常重要的属性:InputBuffer和OutputBuffer。
// 输入缓冲,headerBufferSize默认为 8 * 1024;
inputBuffer = new InternalInputBuffer(request, headerBufferSize, rejectIllegalHeaderName, httpParser);
// 输出缓冲
outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
Http11Processor是socket处理器,而处理的东西无非就是数据,在tomcat中Http11Processor不是直接的去读socket中的inputstream数据,而是先会将inputstream的数据缓存在inputbuffer中,Http11Processor会从inputbuffer中去读取数据,当然在读取时发现数据流没有结束会触发逻辑从inputstream中加载新数据到inputbuffer中。
outputbuffer类似,是输出数据的一种缓冲。
关于这两个在另外的文章中再详细分析,这里只需要知道,Http11Processor会从inputbuffer中获取数据来进行处理。
Http11Processor继承AbstractHttp11Processor类,而处理socket的process()方法在AbstractHttp11Processor中。
AbstractHttp11Processor.process方法
process方法的目的就是将socket中的数据转换成对象,什么对象呢?我们非常熟悉的Request对象,但是需要注意的是,它暂时还不是Servlet中的对象,而是Tomcat自定义的,全名叫org.apache.coyote.Reqeust。
org.apache.coyote.Reqeust
该对象和我们所理解的请求并有没有区别,也分为请求行,请求头,请求体。
比如它的属性:
- protoMB:表示请求协议
- methodMB:表示请求方法
- unparsedURIMB:表示原始的URI
- queryMB:表示查询参数
- uriMB:表示去掉查询参数后的URI
- headers:请求头
在AbstractHttp11Processor.process方法中会执行如下流程:
- getInputBuffer().parseRequestLine(keptAlive):取inputbuffer中的数据解析为Request中的请求行信息
- getInputBuffer().parseHeaders():继续取inputbuffer中的数据解析为Request中的请求头信息
- prepareRequest():请求行和请求头都解析出来后就可以进行验证Tomcat中的配置是否支持该请求了
- adapter.service(request, response):验证过后将request教给下游系统去进行处理,adapter是在Connector初始化的时候被初始化的,实现类为CoyoteAdapter
所以我们接下来看CoyoteAdapter是怎么处理request的。
CoyoteAdapter
service方法
service方法执行流程如下:
- 将org.apache.coyote.Reqeust包装为org.apache.catalina.connector.Request对象
- 将org.apache.catalina.connector.Request的属性进行映射,会生成mappingData
connector.getService().getContainer().getPipeline().getFirst().invoke(
request, response):这个方法就是最关键的方法,找到Connector所属的Service下的Container,Container具体的实现类为StandardEngine,然后获取StandardEngine下的Pipeline,然后执行invoke方法。
所以对于Request的处理被转移到了StandardEngine下的Pipeline中
StandardEngine
StandardEngine是一种容器Container,在Tomcat中有四种容器,这里的容器就可以理解为Servlet容器了,只不过Servlet可以按不同级别进行划分,比如同一个host下的所有servlet划分到一个host容器中,不同host下运行在同一个tomcat中的的可以划分到engine容器中,所以StandardEngine可以理解为一个全局的容器,它的底下肯定需要Host容器。
StandardEngine的构造方法中会去设置一个基础的pipeline。
pipeline.setBasic(new StandardEngineValve());
pipeline是管道,是属于容器中的,在管道中可以添加阀门valve,所以上面的代码是StandardEngine给自己设置了一个基础阀门。
那我们接下来看一下这个基础阀门StandardEngineValve是怎么处理我们的Request的。
StandardEngineValve
StandardEngineValve中的invoke方法执行流程如下:
- Host host = request.getHost():从请求中获取对应的host,实际上获取的是mappingData中的host。
- host.getPipeline().getFirst().invoke(request, response):然后使用Host容器中的第一个管道来执行请求
host对象是也是server.xml中配置的,会在CoyoteAdapter的postParseRequest方法中调用Mapper类的map方法将host对象设置到mappingData中。
Host是一个接口,它的默认实现类是StandardHost。
StandardHost
StandardHost是虚拟域名级别的servlet容器,在它的构造方法中会调用如下代码设置valve
pipeline.setBasic(new StandardHostValve());
要注意的是,在server.xml中其实也配置了一个valve,这个valve才是host管道中的第一个valve:
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
关于valve的级联后文再介绍,在处理请求时StandardHostValve才是重点。
StandardHostValve
StandardHostValve的invoke方法会从request中获取到Context对象,然后使用Context中的pipeline处理请求:
context.getPipeline().getFirst().invoke(request, response);
Context是一个接口,默认的实现类为StandardContext。StandardContext中最重要的valve是StandardContextValve。
StandardContextValve
StandardContextValve的invoke方法会从request中获取到Wrapper对象,然后使用Wrapper中的pipeline处理请求:
wrapper.getPipeline().getFirst().invoke(request, response);
Wrapper是一个接口,默认的实现类为StandardWrapper,StandardWrapper中最重要的valve是StandardWrapperValve
StandardWrapperValve
StandardWrapperValve中会从Wrapper中拿出Servlet,并将Servlet加入到filter链中:
ApplicationFilterChain filterChain =
factory.createFilterChain(request, wrapper, servlet);
然后执行filter链,最终可以执行到servlet中的service方法:
filterChain.doFilter(request.getRequest(), response.getResponse());
到此,一个请求的处理流程大概就是这样,只不过其中还有很多问题?比如:
- Host的初始化
- Context的初始化
- Wrapper的初始化
- 各个组件的pipeline的初始化
- filter链的初始化