https://www.cnblogs.com/junzisi/p/15616519.html
原文链接:https://blog.csdn.net/m0_63437643/article/details/123068012
### spring boot创建tomcat容器
起点
-> SpringApplication.run()
-> SpringApplication.refreshContext()
-> AbstractApplicationContext.refresh()
-> AbstractApplicationContext.onRefresh()
第一步:spring boot启动时,容器初始化、启动 重点:仅tomcat容器启动,连接未启动
-> AbstractApplicationContext.onRefresh()
-> ServletWebServerApplicationContext.onRefresh()
createWebServer()
-> ServletWebServerApplicationContext.createWebServer() createWebServer方法会将
创建的webServer封装在WebServerStartStopLifecycle对象中,
并注册到Spring容器中。
ServletWebServerFactory factory = getWebServerFactory() getWebServerFactory会创建
tomcatServletWebServerFactory、
DispatcherServlet、
DispatcherServletRegistrationBean 等bean放在ioc容器中
this.webServer = factory.getWebServer(getSelfInitializer())
-> ServletWebServerFactory.getWebServer() ServletWebServerFactory的实现有tomcat、jetty、undertow
-> TomcatServletWebServerFactory.getWebServer() 首先创建Tomcat,创建连接器Connector、创建协议、
创建端点…创建容器Container,如StandardServer、…
Tomcat tomcat = new Tomcat()
Connector connector = new Connector(this.protocol)
tomcat.getService().addConnector(connector)
...
-> TomcatServletWebServerFactory.getTomcatWebServer(tomcat)
-> new TomcatWebServer(tomcat, getPort() >= 0, getShutdown())
initialize()
-> TomcatWebServer.initialize() 仅初始化:初始化Connector(Connector的内部类initInternal())时,
会创建协议适配器CoyoteAdapter,它是连接Connector和Container的桥梁,
具体就是:适配器持有Connector属性,协议持有适配器,
连接器持有容器(Service)。
初始化协议
初始化端点
初始化容器
tomcat.start() 启动服务,触发初始化监听
重要步骤:把DispatcherServlet注册到StandardWraper中
TomcatWebServer 的 initialize 方法中做了以下操作:重命名tomcat 弓|擎名称、对 Context 添加生 命周期监听事件、启动服务触发初始化监听、检查TomcatStarter 对象是否存在及 Container 状态是否正确、绑定命名到类加载器、启动守护等待线程等。
容器逐层start采用的是异步方式,但提交异步任务之后会同步等待子容器start完成,效果相当于同步完成。
第二步:spring boot启动时,协议端点的初始化 tomcat连接启动
->AbstractApplicationContext.finishRefresh()
protected void finishRefresh() {
// Initialize lifecycle processor for this context.
// 初始化生命周期处理器。这个bena也是有固定名称的
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
// 启动生命周期处理器
getLifecycleProcessor().onRefresh();
// Publish the final event.
// 发布 ContextRefreshedEvent 事件
publishEvent(new ContextRefreshedEvent(this));
}
调用getLifecycleProcessor().onRefresh()
-> org.springframework.boot.web.servlet.context.WebServerStartStopLifecycle.start()
SpringBoo中就是通过实现SmartLifecycle来启动内嵌的web容器,
实现类为WebServerStartStopLifecycle。
public void start() {
this.webServer.start();
this.running = true;
this.applicationContext
.publishEvent(new ServletWebServerInitializedEvent(this.webServer, this.applicationContext));
}
-> TomcatWebServer.start()
-> TomcatWebServer.addPreviouslyRemovedConnectors()
-> StandardService.addConnector()
public void addConnector(Connector connector) {
synchronized (connectorsLock) {
connector.setService(this);
Connector results[] = new Connector[connectors.length + 1];
System.arraycopy(connectors, 0, results, 0, connectors.length);
results[connectors.length] = connector;
connectors = results;
}
try {
if (getState().isAvailable()) {
connector.start(); 启动连接
}
} catch (LifecycleException e) {
throw new IllegalArgumentException( );
}
}
-> LifecycleBase.startInternal()
-> Connector.startInternal()
protocolHandler.start() 启动协议
-> AbstractProtocol.startInternal()
endpoint.start()
-> AbstractEndpoint.start()
-> NioEndpoint.startInternal() 启动端点
createExecutor() 线程池
initializeConnectionLatch()
poller = new Poller(); Poller线程
startAcceptorThread() Acceptor线程监听客户端请求
第三步:监听客户端请求
Acceptor处理客户端连接
主要用于接收网络请求,建立连接,连接建立之后,将一个SocketChannel对象包装成一个NioChannel,并注册到Poller中。由Poller来负责执行数据的读取和业务执行。
Acceptor的作用是控制与tomcat建立连接的数量,但Acceptor仅仅负责建立连接。AbstractEndpoint有个maxConnections属性,用来规定server所能接受的最大连接数。
默认是8192。在NioEndpoint初始化的时候,会使用maxConnections来初始化一个LimitLatch,使用这个LimitLatc来进行并发请求连接的管理。
### Acceptor与三次握手
backlog=syn+accept=accpetCount
对于client端的一个请求进来,流程是这样的:tcp的三次握手建立连接,建立连接的过程中,OS维护了半连接队列(syn队列)以及完全连接队列(accept队列),
在第三次握手之后,server收到了client的ack,则进入establish的状态,然后该连接由syn队列移动到accept队列。
tomcat的Acceptor线程则负责从accept队列中取出该connection,然后交给工作线程去处理(读取请求参数、处理逻辑、返回响应等等;
如果该连接不是keep alived的话,则关闭该连接,然后该工作线程释放回线程池,如果是keep alived的话,则等待下一个数据包的到来直到keepAliveTimeout,
然后关闭该连接释放回线程池),然后自己接着去accept队列取connection(当当前socket连接超过maxConnections的时候,Acceptor线程自己会阻塞等待,等连接降下去之后,
才去处理accept队列的下一个连接)。
### Acceptor与限流器
执行时,使用while(true)来进行循环。主要是进行serverSocket.accept的监听。当没有连接请求来时,线程会堵塞在accept方法上。
当有新的请求到达时,使用countUpOrAwaitConnection来管理连接数。假设连接数还没有达到最大值。就将LimitLatch的值加1,LimitLatch的值就表示了当前与服务端建立连接的个数,
socket成功执行就调用desctorySocket方法,进而调用countDownConnection方法减一,socket异常时直接调用countDownConnection减一。假设已经达到了最大值,则让线程等待。
### Poller工作线程进行业务处理
在端点的startInternal方法中启动,由pollerThreadCount属性控制,默认是2。
socket内容的读写是通过Poller来实现的。Poller使用java nio来实现连接的管理。封装后SocketChannel放入Poller线程内部维护的一个PollerEvent队列中,
然后在Poller线程运行时处理队列,将SocketChannel注册到这个Poller的Selector上。
当数据到来的时候,Selector通过selector.select系列方法来获取数据,然后经由processKey到processSocket方法,封装成一个SocketProcessor对象后,放在EndPoint的线程池中执行。
SocketProcessor
SocketProcessor中调用协议的ConnectionHandler,它内部调用Processor,Processor内部在调用CoyoteAdaptor完成请求转换并调用到Container中。
### Processor
### CoyoteAdaptor
管道任务
一个servlet容器可以有一条管道。当调用了容器的invoke()方法后,容器将处理工作交由管道完成,而管道会调用其中的第一个阀开始处理。当第一个阀处理完后,它会调用后续的阀继续执行任务,直到管道中所有的阀都处理完成,基础阀总是最后一个执行的。