Tomcat中的JioEndpoint负责socket的接口绑定,JioEndpoint中的Acceptor负责接收socket请求,当接收到一个socket请求后,将执行如下流程:

JioEndpoint.proccessSocket方法

该方法主要完成如下步骤:

  1. 将socket进行包装,包装为SocketWrapper;
  2. 将SocketWrapper包装为SocketProcessor,SocketProcessor实现了Runnable
  3. 将SocketProcessor扔进线程池,等待线程池的调度

SocketProcessor执行流程

SocketProcessor是一个线程对象,在run方法中主要完成以下两件事情:

  1. serverSocketFactory.handshake(socket.getSocket()):进行ssl的握手
  2. handler.process(socket, SocketStatus.OPEN_READ):使用handler处理socket,这里的handler就是Http11Protocol类的内部类Http11ConnectionHandler。

所以,真正处理socket的逻辑转移到了Http11ConnectionHandler中。

Http11ConnectionHandler

Http11ConnectionHandler继承了AbstractConnectionHandler类,process方法在AbstractConnectionHandler中。

AbstractConnectionHandler中的process方法执行流程:

  1. Socket socket = wrapper.getSocket():从SocketWrapper中获取被包装的原始Socket
  2. Processor processor = createProcessor(); 创建一个Processor,具体创建的逻辑在Http11ConnectionHandler类中实现
  3. state = processor.process(wrapper):使用Processor处理socketWrapper

所以,真正处理socket的逻辑转移到了Processor中。

Http11Processor

Http11ConnectionHandler的createProcessor方法会创建出来一个Http11Processor对象。在创建Http11Processor时,会把Http11Protocol中很多属性同步到Http11Processor属性中,同时还会初始化Http11Processor中还两个非常重要的属性:InputBuffer和OutputBuffer。

  1. // 输入缓冲,headerBufferSize默认为 8 * 1024;
  2. inputBuffer = new InternalInputBuffer(request, headerBufferSize, rejectIllegalHeaderName, httpParser);
  3. // 输出缓冲
  4. 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方法中会执行如下流程:

  1. getInputBuffer().parseRequestLine(keptAlive):取inputbuffer中的数据解析为Request中的请求行信息
  2. getInputBuffer().parseHeaders():继续取inputbuffer中的数据解析为Request中的请求头信息
  3. prepareRequest():请求行和请求头都解析出来后就可以进行验证Tomcat中的配置是否支持该请求了
  4. adapter.service(request, response):验证过后将request教给下游系统去进行处理,adapter是在Connector初始化的时候被初始化的,实现类为CoyoteAdapter

所以我们接下来看CoyoteAdapter是怎么处理request的。

CoyoteAdapter

service方法

service方法执行流程如下:

  1. 将org.apache.coyote.Reqeust包装为org.apache.catalina.connector.Request对象
  2. 将org.apache.catalina.connector.Request的属性进行映射,会生成mappingData
  3. connector.getService().getContainer().getPipeline().getFirst().invoke(

    1. request, response):这个方法就是最关键的方法,找到Connector所属的Service下的ContainerContainer具体的实现类为StandardEngine,然后获取StandardEngine下的Pipeline,然后执行invoke方法。

所以对于Request的处理被转移到了StandardEngine下的Pipeline中

StandardEngine

StandardEngine是一种容器Container,在Tomcat中有四种容器,这里的容器就可以理解为Servlet容器了,只不过Servlet可以按不同级别进行划分,比如同一个host下的所有servlet划分到一个host容器中,不同host下运行在同一个tomcat中的的可以划分到engine容器中,所以StandardEngine可以理解为一个全局的容器,它的底下肯定需要Host容器。

StandardEngine的构造方法中会去设置一个基础的pipeline。

  1. pipeline.setBasic(new StandardEngineValve());

pipeline是管道,是属于容器中的,在管道中可以添加阀门valve,所以上面的代码是StandardEngine给自己设置了一个基础阀门。

那我们接下来看一下这个基础阀门StandardEngineValve是怎么处理我们的Request的。

StandardEngineValve

StandardEngineValve中的invoke方法执行流程如下:

  1. Host host = request.getHost():从请求中获取对应的host,实际上获取的是mappingData中的host。
  2. host.getPipeline().getFirst().invoke(request, response):然后使用Host容器中的第一个管道来执行请求

host对象是也是server.xml中配置的,会在CoyoteAdapter的postParseRequest方法中调用Mapper类的map方法将host对象设置到mappingData中。

Host是一个接口,它的默认实现类是StandardHost。

StandardHost

StandardHost是虚拟域名级别的servlet容器,在它的构造方法中会调用如下代码设置valve

  1. pipeline.setBasic(new StandardHostValve());

要注意的是,在server.xml中其实也配置了一个valve,这个valve才是host管道中的第一个valve:

  1. <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
  2. prefix="localhost_access_log." suffix=".txt"
  3. pattern="%h %l %u %t &quot;%r&quot; %s %b" />

关于valve的级联后文再介绍,在处理请求时StandardHostValve才是重点。

StandardHostValve

StandardHostValve的invoke方法会从request中获取到Context对象,然后使用Context中的pipeline处理请求:

  1. context.getPipeline().getFirst().invoke(request, response);

Context是一个接口,默认的实现类为StandardContext。StandardContext中最重要的valve是StandardContextValve。

StandardContextValve

StandardContextValve的invoke方法会从request中获取到Wrapper对象,然后使用Wrapper中的pipeline处理请求:

  1. wrapper.getPipeline().getFirst().invoke(request, response);

Wrapper是一个接口,默认的实现类为StandardWrapper,StandardWrapper中最重要的valve是StandardWrapperValve

StandardWrapperValve

StandardWrapperValve中会从Wrapper中拿出Servlet,并将Servlet加入到filter链中:

  1. ApplicationFilterChain filterChain =
  2. factory.createFilterChain(request, wrapper, servlet);

然后执行filter链,最终可以执行到servlet中的service方法:

  1. filterChain.doFilter(request.getRequest(), response.getResponse());

到此,一个请求的处理流程大概就是这样,只不过其中还有很多问题?比如:

  1. Host的初始化
  2. Context的初始化
  3. Wrapper的初始化
  4. 各个组件的pipeline的初始化
  5. filter链的初始化