请求
请求时由Connector来处理。从启动连接器开始:
启动连接器
连接器构造方法中实例化了一个ProtocolHandler协议处理器,用来处理请求。然后在启动方法中启动协议处理器。
1 |
|
然后在AbstractProtocol中启动endpoint:
启动endpoint
1 | public void start() throws Exception { |
endpoint的实现有几种:
比如:NioEndPoint
NioEndPoint
1 | /** |
中定义了内部类,通过线程方式处理。
Acceptor接收请求
1 | protected class Acceptor extends AbstractEndpoint.Acceptor { |
调用setSocketOptions方法
1 | //处理指定的连接 |
Poller
1 |
|
1 | protected abstract SocketProcessorBase<S> createSocketProcessor(SocketWrapperBase<S> socketWrapper, SocketEvent event); |
createSocketProcessor方法是抽象方法,去他的处理器实现:
AbstractHttp11Protocol extends AbstractProtocol
1 |
|
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
通过连接器获得service可以获取容器(Engine只有一个),再获取调用链。在Engine(StandardEngine)启动后 pipeline也会启动,维护的是StandardEngineValve
invoke方法是StandardEngineValve中的invoke方法。
1 | public final void invoke(Request request, Response response) |
StandardEngineValve中的invoke又会调用host中的StandardHostValve的invoke方法,依次StandardContextValve->StandardWrapperValve,最后到StandardWrapperValve中的invoke,这里处理servlet。
1 |
|
一直到找到servlet
实例化servlet
allocate()方法:
1 |
|
处理
上面最终通过连接器的适配器执行service:
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
最终可以找到servlet处理。
处理完成,层层返回:
1 | //请求响应完成 |
请求时序图:
总结accept流程
Connector 启动以后会启动一组线程用于不同阶段的请求处理过程,Acceptor、Poller、worker 所在的线程组都维护在 NioEndpoint 中。
Acceptor线程组。用于接受新连接,并将新连接封装一下,选择一个 Poller 将新连接添加到 Poller 的事件队列中,Acceptor线程组是多个线程组成的线程组。
Poller 线程组。用于监听 Socket 事件,当 Socket 可读或可写等等时,将 Socket 封装一下添加到 worker 线程池的任务队列中,Poller线程组是多个线程组成的线程组。
worker 线程组。用于对请求进行处理,包括分析请求报文并创建 Request 对象,调用容器的 pipeline 进行处理,worker线程组是Executor创建的线程池。
1 | public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> { |
请求处理过程
Acceptor接收连接
Acceptor接受的新连接没有立即注册到selector当中,需要先封装成PollerEvent对象后保存至PollerEvent队列当中,Poller对象会消费PollerEvent队列,类似生产消费模型。
Acceptor 在启动后会阻塞在 ServerSocketChannel.accept(); 方法处,当有新连接到达时,该方法返回一个 SocketChannel。
setSocketOptions()方法将 Socket 封装到 NioChannel 中,并注册到 Poller。
一开始就启动了多个 Poller 线程,注册的时候采用轮询选择 Poller 。NioEndpoint 维护了一个 Poller 数组,当一个连接分配给 pollers[index] 时,下一个连接就会分配给 pollers[(index+1)%pollers.length]。
addEvent() 方法会将 Socket 添加到该 Poller 的 PollerEvent 队列中。到此 Acceptor 的任务就完成了。
1 | public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> { |
Poller处理请求
Poller会消费PollerEvent队列(由Acceptor进行投递),并注册到Selector当中。当注册到Selector的socket数据可读的时候将socket封装成SocketProcessor对象,投递到Executor实现的线程池进行处理。
selector.select(1000)。当 Poller 启动后因为 selector 中并没有已注册的 Channel,所以当执行到该方法时只能阻塞。所有的 Poller 共用一个 Selector,其实现类是 sun.nio.ch.SelectorImpl。
events() 方法通过 addEvent() 方法添加到事件队列中的 Socket 注册到SelectorImpl。这里指的socket是accept过来的请求的socket。
当 Socket 可读时,Poller 才对其进行处理,createSocketProcessor() 方法将 Socket 封装到 SocketProcessor 中,SocketProcessor 实现了 Runnable 接口。worker 线程通过调用其 run() 方法来对 Socket 进行处理。
execute(SocketProcessor) 方法将 SocketProcessor 提交到线程池,放入线程池的 workQueue 中。workQueue 是 BlockingQueue 的实例。
1 | public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> { |
Worker处理具体请求
当新任务添加到 workQueue(ThreadPoolExecutor)后,workQueue.take()方法会返回一个 Runnable,通常是 SocketProcessor,然后 worker 线程调用 SocketProcessor的run() -> doRun()方法对 Socket 进行处理。
createProcessor() 会创建一个Http11Processor, 它用来解析 Socket,将 Socket 中的内容封装到Request中。注意这个Request是临时使用的一个类,它的全类名是org.apache.coyote.Request。
CoyoteAdapter的postParseRequest()方法封装一下 Request,并处理一下映射关系(从 URL 映射到相应的 Host、Context、Wrapper)。
CoyoteAdapter将 Rquest 提交给 Container(StandardEngine) 处理之前,并将 org.apache.coyote.Request封装到 org.apache.catalina.connector.Request,传递给 Container处理的 Request 是 org.apache.catalina.connector.Request。
connector.getService().getMapper().map(),用来在Mapper中查询 URL 的映射关系。映射关系会保留到 org.apache.catalina.connector.Request 中,Container处理阶段 request.getHost()是使用的就是这个阶段查询到的映射主机,以此类推 request.getContext()、request.getWrapper()都是。
connector.getService().getContainer().getPipeline().getFirst().invoke()会将请求传递到 Container(StandardEngine)处理,至此进入了Engine->Host->Context->Wrapper的处理流程,当然了 Container处理也是在 Worker线程中执行的(也就是说Tomcat处理请求是通过ThreadPoolExecutor的线程池实现的),但是这是一个相对独立的模块,所以单独分出来一节。