Tomcat-8.5.57版本

1、总体架构

Server

Service

Connector

Engine

Host

Context

Wrapper

2、线程模型

Tomcat创建Acceptor线程用于accept()阻塞接收客户端连接,将请求封装为PollerEvent对象加入Poller其中一个线程的events队列中,在Poller线程中将events队列中的事件注册到自己的选择器epoll中,并且select()其中准备好的客户端连接,根据读写状态位来进行处理。

image-20240622111957489

3、生命周期

Lifecycle接口

LifecycleBase抽象类

对接口默认实现,并且在init()start()stop()destroy()中都进行了状态切换,并且通知了观察者

子类子需要实现initInternal()startInternal()stopInternal()destroyInternal()

LifecycleMBeanBase

Container容器

Lifecycle的基础上还需要包含一些其他事件时比如:添加移除子容器、添加移除通道pipelines

EngineHostContextWrapper这四种都属于容器。

ContainerBase抽象类

Bootstrap启动类

Tomcat脚本启动时,会调用Bootstrap类的main方法。而其start实现如下

其实就是调用Catalinaloadstart方法。最后调用StandardServerinit()start()方法。至此整个生命周期就开始了。下面就可以从StandardServer开始研究其到底干了什么事,是如何处理客户端请求的。

4、StandardServer服务类

主要功能:

实现shutdown 服务的监听。

initInternal

调用StandardServiceinit()方法。

startInternal

调用StandardServicestart()方法。

5、StandardService管理类

主要功能:

管理EngineExecutorMapperListenerConnector

管理的组件:

Executor:StandardThreadExecutor

为什么这样设计:当核心线程满时,如果客户端请求来了,此时将请求加入阻塞队列,那么会出现大量500、超时。

MapperListener

startInternal()方法中会将其加入到所有组件、容器的观察者中,用于监听其创建和销毁。当创建时,将其对应的路径设置到mapper中,以后一个链接过来时,就可以根据mapper中的路径找到对应的HostContextWrapper等。

initInternal

startInternal

6、Connector连接器

主要功能:

Connector就是使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型,比如:Http11Protocol使用的是普通Socket来连接的, Http11NioProtocol使用的是NioSocket来连接的。

其中ProtocolHandler包含了三个部件:EndpointProcessorAdapter

  1. Endpoint用来处理底层Socket的网络连接,Processor用于将 Endpoint接收到的Socket封装成Request,Adapter用于将Request交给 Container进行具体的处理。

  2. Endpoint由于是处理底层的Socket网络连接,因此Endpoint是用来实现TCP/IP协议的,而Processor用来实现HTTP协议的,Adapter将请求适配到Servlet容器进行具体的处理。

  3. Endpoint的抽象实现AbstractEndpoint里面定义的Acceptor和AsyncTimeout两个内部类和一个Handler接口。Acceptor用于监听请求,AsyncTimeout用于检查异步Request的超时,Handler用于处理接收到的Socket,在内部调用Processor进行处理。

initInternal

其中会调用Http11NioProtocolinit()方法。Http11NioProtocol其包装了网络IO、和HTTP解码等。最终会调用NioEndpoint.init()方法调用bind创建ServerSocketChannel

startInternal

调用Http11NioProtocolstart()方法。protocolHandler.start()方法会调用NioEndpoint.start()方法。最后调用NioEndpoint.startInternal()。其中会创建专门接收客户端连接的Acceptor线程组和接收读写请求的Poller线程组。

NioEndpoint.Acceptor

run方法:

首先控制连接数,如果当前达到最大连接数就会阻塞不在accept()新的连接,否则接收连接并且setSocketOptions()方法中设置客户端属性,并且负载均衡获取一个Poller将其注册到events队列中。

NioEndpoint.Poller

run方法:

处理events中的事件,即向epoll中注册read事件。selector.select获取已经准备好的事件集,然后根据事件类型processSocket进行处理。

wakeupCounter:

SocketProcessor.doRun

如果已经完成三次握手,则开始进入连接的生命周期即ConnectionHandler类中

ConnectionHandler

创建Http11Processor,并调用process方法

Http11Processor.service

解析http,并且检测并设置错误码400、500、502,调用适配器,请求开始进入Tomcat的容器

CoyoteAdapter.service

封装TomcatRequestResponseHttpServletRequestHttpServletResponse。最后调用EnginePipelinePipeline工作原理下文介绍。

请求工作原理:

至此我们知道,Tomcat如何接收、处理请求:

  1. Connectorstart生命周期中,创建AcceptorPoller线程组,并执行run方法。

  2. Acceptor中接收客户端连接并将其负载均衡到Poller中的events队列中。

  3. Poller在循环中首先将events中的事件注册到自己的epoll中。

  4. Poller开始select已经准备好的请求,并且创建SocketProcessor将其放入线程池中异步执行。

  5. SocketProcessorrun方法会调用ConnectionHandler开始处理请求的生命周期包含资源的释放等。

  6. ConnectionHandler最后调用Http11Processor将请求解析,最后调用CoyoteAdapter

  7. CoyoteAdapter将请求封装为HttpServletRequestHttpServletResponse。传入Engine容器的Pipeline中。

 

7、Pipeline责任链

总体流程如上,只需要研究StandardWrapperValve干了什么即可。

StandardWrapperValve

首先通过所有Filter,之后调用servlet.service,根据请求类型走不同的方法如:doPostdoGetdoDelete

8、Write原理

上一节,主要讲了一个Tomcat如何获取读请求到封装Servlet的整个过程,我们发现在Epoll中,写请求未作任何处理,主要是因为写请求不Poller中的epoll中进行检测,而是有自己独立的Epoll,这一节就主要来研究其工作原理。

我们一般使用这种方式会写数据,接下来主要研究其底层到底干了什么。

Response

方法返回了一个CoyoteOutputStream对象,其中封装了Java nio的操作。将数据写入堆缓存中。主要是mark、position、limit、capacity四个变量。就不详细解释了,主要看flush是干了什么。

OutputBuffer

最终会调用NioEndpointdoWrite方法。

NioEndpoint

NioSelectorPool

如果网络的写缓冲区满了不可写了,那么就需要特殊处理,将注册一个epoll的写事件,等到可写时再进行写入操作。