- 1. Springboot Tomcat 架构及参数优化
1. Springboot Tomcat 架构及参数优化
1.1. 版本说明
构件 | 版本 |
---|---|
spring-boot | 2.7.18 |
tomcat-embed-core | 9.0.83 |
1.2. SpringBoot Tomcat 架构分析
1.2.1. Tomcat 核心组件类图
classDiagram direction LR class Tomcat { #Server server +void start() +void stop() } class Connector { #ProtocolHandler protocolHandler } class Container { <<Interface>> } class Engine { <<Interface>> } class StandardEngine class Host { <<Interface>> } class StandardHost class Lifecycle { <<Interface>> void start() void stop() } class Server { <<Interface>> } class StandardServer { -Service[] services } class Service { <<Interface>> } class StandardService { #Connector[] connectors -Engine engine } class AbstractHttp11JsseProtocol~S~ class AbstractHttp11Protocol~S~ class AbstractProtocol~S~ { -AbstractEndpoint endpoint } class Http11NioProtocol class Http11Nio2Protocol class ProtocolHandler { <<Interface>> void start() void stop() } class AbstractEndpoint~S, U~ { -Executor executor -LimitLatch connectionLimitLatch -Handler~S~ handler +void bind() #U serverSocketAccept() +void createExecutor() +boolean processSocket(SocketWrapperBase~S~> socketWrapper, SocketEvent event, boolean dispatch) +void start() +void stop() } class AbstractJsseEndpoint~S, U~ class Nio2Endpoint class NioEndpoint { -ServerSocketChannel serverSock } class Executor { <<Interface>> } class ThreadPoolExecutor note for ThreadPoolExecutor "Tomcat 线程池在 AbstractEndpoint 创建,server.tomcat.threads.max、\n server.tomcat.threads.min-spare 参数作用于此,\n 用于指定最大、最小线程数,线程池其他参数默认值为:\n 是否守护线程:是;\n 线程优先级:5;\n 空闲线程存活时间:60 秒;\n 任务队列为:TaskQueue,容量为 Integer.MAX_VALUE" class VirtualThreadExecutor class LimitLatch { -AtomicLong count -long limit +long countDown() +void countUpOrAwait() } note for LimitLatch "LimitLatch 用于限制连接数量,基于 AQS 实现,\n server.tomcat.max-connections 参数作用于此,\n 接受一个连接前判断是否达到最大连接数 limit,\n 否则自旋等待直至成功并 count 加 1;\n 关闭连接后 count 减 1" class Handler { <<Interface>> } class ConnectionHandler~S~ class Poller note for Poller "Poller 线程不停从已连接的 socket 读取事件,\n 最终封装成 SocketProcessorBase 交给 ThreadPoolExecutor 处理" class Acceptor note for Acceptor "Acceptor 线程不停接收新的客户端连接,\n 直至达到 server.tomcat.max-connections" class SocketProcessor class SocketProcessorBase~S~ note for SocketProcessorBase "SocketProcessorBase 线程将请求经过层层传递最终给到\n DispatcherServlet,DispatcherServlet 再分派到对应的\n Spring Controller 中处理具体的业务逻辑" class Runnable { <<Interface>> } Tomcat "1" *--> "1" Host Tomcat "1" *--> "1" Server Host --|> Container StandardHost ..|> Host StandardServer ..|> Server StandardServer "1" *--> "n" Service Server --|> Lifecycle Service --|> Lifecycle StandardService ..|> Service StandardService "1" *--> "1" Engine StandardService "1" *--> "n" Connector StandardEngine ..|> Engine Engine --|> Container Container --|> Lifecycle Connector ..|> Lifecycle Connector "1" *--> "1" ProtocolHandler Http11NioProtocol --|> AbstractHttp11JsseProtocol Http11Nio2Protocol --|> AbstractHttp11JsseProtocol AbstractHttp11JsseProtocol --|> AbstractHttp11Protocol AbstractHttp11Protocol --|> AbstractProtocol AbstractProtocol ..|> ProtocolHandler AbstractProtocol "1" *--> "1" AbstractEndpoint AbstractJsseEndpoint --|> AbstractEndpoint Nio2Endpoint --|> AbstractJsseEndpoint NioEndpoint --|> AbstractJsseEndpoint AbstractEndpoint "1" *--> "1" Executor AbstractEndpoint "1" *--> "1" LimitLatch AbstractEndpoint "1" *--> "1" Handler AbstractEndpoint ..> SocketProcessorBase ThreadPoolExecutor ..|> Executor VirtualThreadExecutor ..|> Executor ConnectionHandler ..|> Handler Poller ..|> Runnable Acceptor ..|> Runnable Poller ..> AbstractEndpoint Acceptor ..> AbstractEndpoint SocketProcessor --|> SocketProcessorBase~S~ SocketProcessorBase~S~ ..|> Runnable1.2.2. Tomcat 核心组件架构图
C4Component title Tomcat 核心组件架构图 Container_Boundary(Tomcat, "Tomcat") { Container_Boundary(Server, "Server") { Container_Boundary(Service, "Service") { Container_Boundary(Connector, "Connector") { Container_Boundary(Http11NioProtocol, "Http11NioProtocol") { Container_Boundary(NioEndpoint, "NioEndpoint") { Component(LimitLatch, "LimitLatch", "限流") Component(Acceptor, "Acceptor", "接收 socket 连接") Component(Poller, "Poller", "监听 socket 事件") Container_Boundary(ThreadPoolExecutor, "ThreadPoolExecutor") { Component(SocketProcessor, "SocketProcessor", "处理请求") } } } } } Container_Boundary(Engine, "Engine") { Component(Host, "Host", "") } } }1.3. SpringBoot Tomcat 工作流程
1.3.1. SpringBoot 初始化 Tomcat 流程
flowchart TD flow1("SpringApplication#run(Class<?> primarySource, String... args)") --> flow2("SpringApplication#run(Class<?>[] primarySources, String[] args)") flow2 --> flow3("SpringApplication#run(String... args)") flow3 --> flow4("SpringApplication#refreshContext(ConfigurableApplicationContext context)") flow4 --> flow5("SpringApplication#refresh(ConfigurableApplicationContext applicationContext)") flow5 --> flow6("ServletWebServerApplicationContext#refresh()") flow6 --> flow7("AbstractApplicationContext#refresh()") flow7 --> flow8("ServletWebServerApplicationContext#onRefresh()") flow8 --> flow9("ServletWebServerApplicationContext#createWebServer()") subgraph flow10["TomcatServletWebServerFactory#getWebServer(ServletContextInitializer... initializers)"] direction LR flow10_1("Tomcat#Tomcat()") flow10_1 --> flow10_2("Tomcat#start()") end flow9 --> flow101.3.2. Tomcat 启动流程
flowchart TD flow1["Tomcat#start()"] flow2["StandardServer#start()"] flow3["StandardService#start()"] flow4["StandardEngine#start()"] flow5["Connector#start()"] flow6["Http11NioProtocol#start()"] subgraph flow7["NioEndpoint#start()"] flow8["NioEndpoint#bind() \n 初始化 ServerSocketChannel,绑定端口"] flow9["NioEndpoint#createExecutor() \n 创建工作线程池"] flow10["NioEndpoint#initializeConnectionLatch() \n 初始化限流组件"] flow11["Poller#Poller() \n 启动 socket 事件监听线程"] flow12["Acceptor#Acceptor() \n 启动 socket 连接线程"] end flow1 --> flow2 flow2 --> flow3 flow3 --> flow4 flow3 --> flow5 flow5 --> flow6 flow6 --> flow7 flow7 --> flow8 flow8 --> flow9 flow9 --> flow10 flow10 --> flow11 flow11 --> flow121.3.2.1. 初始化 ServerSocketChannel
核心源码:
serverSock = ServerSocketChannel.open();
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
serverSock.bind(addr, getAcceptCount());
- 打开 ServerSocketChannel。
- 绑定端口,指定 backlog
其中端口由 server.port
配置参数指定,backlog 由 server.tomcat.accept-count
配置参数指定,默认值为 100,客户端与服务端完成 TCP 三次握手之后,连接放入等待队列中,ServerSocketChannel 调用 accept() 方法从队列中取出连接。因此,当 Tomcat 达到 max-connections
指定的最大连接数后,还能继续接收 accept-count
数量的连接。
1.3.2.2. 初始化工作线程池
核心源码:
TaskQueue taskqueue = new TaskQueue();
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
TaskQueue
任务队列继承自LinkedBlockingQueue
,这里无法指定容量,默认容量为Integer.MAX_VALUE
,即无限大。预计未来将支持指定容量,详见 github issues。TaskThreadFactory
线程工厂指定了线程名称前缀为http-nio-端口-
;线程为守护线程;线程优先级为默认值:5。- 线程池核心线程数由
server.tomcat.threads.min-spare
配置参数指定,默认值为 10;线程池最大线程数由server.tomcat.threads.max
配置参数指定,默认值为 200;空闲线程存活时间 60 秒。
TaskQueue
重写了 offer
方法,使得 Tomcat 线程池与 JDK 线程池创建线程的时机不一样,具体表现为:
- 如果线程池里的线程数量等于最大线程数,说明无法再创建新线程,任务加入队列中,等待空闲线程处理。
- 如果已提交的任务数小于等于线程池里的线程数量,说明有空闲线程,任务加入队列中,由空闲线程处理。
- 如果线程池里的线程数量小于最大线程数,任务无法加入队列,强制线程池新建线程去处理。
- 如果以上都不是,任务加入队列,等待空闲线程处理。
核心源码:
@Override
public boolean offer(Runnable o) {
//we can't do any checks
if (parent==null) {
return super.offer(o);
}
//we are maxed out on threads, simply queue the object
if (parent.getPoolSizeNoLock() == parent.getMaximumPoolSize()) {
return super.offer(o);
}
//we have idle threads, just add it to the queue
if (parent.getSubmittedCount() <= parent.getPoolSizeNoLock()) {
return super.offer(o);
}
//if we have less threads than maximum force creation of a new thread
if (parent.getPoolSizeNoLock() < parent.getMaximumPoolSize()) {
return false;
}
//if we reached here, we need to add it to the queue
return super.offer(o);
}
1.3.2.3. 初始化限流组件 LimitLatch
核心源码:
protected LimitLatch initializeConnectionLatch() {
if (maxConnections==-1) {
return null;
}
if (connectionLimitLatch==null) {
connectionLimitLatch = new LimitLatch(getMaxConnections());
}
return connectionLimitLatch;
}
最大连接数由 server.tomcat.max-connections
配置参数指定,默认值为 8192,表示同一时间 Tomcat 能够接受的最大连接数量。接受一个新连接 LimitLatch 计数加 1,处理完请求断开连接,LimitLatch 计数减 1。
1.3.3. Acceptor 线程工作流程
flowchart TD flow1["线程启动"] flow2{"停止?"} flow3["尝试 LimitLatch 计数加 1"] flow4{"成功?"} flow6["SocketChannel socket = endpoint.serverSocketAccept() \n 接收新连接"] flow7["SocketChannel 封装为 NioSocketWrapper"] flow8["NioSocketWrapper 封装为 PollerEvent"] flow9["PollerEvent 注册到 Poller 的 SynchronizedQueue 队列,Poller 线程处理队列里的事件"] flow1 --> flow2 flow2 --> |no|flow3 flow3 --> flow4 flow4 --> |no \n 自旋-等待-重试|flow3 flow4 --> |yes|flow6 flow6 --> flow7 flow7 --> flow8 flow8 --> flow9 flow9 --> flow21.3.4. Poller 线程工作流程
flowchart TD flow1["线程启动"] flow2{"while(true)"} flow3["Poller#events() \n 处理 SynchronizedQueue 队列里的 PollerEvent 事件"] flow4["Selector#selectedKeys() \n 监听 socket 事件"] flow5["Poller#processKey(SelectionKey sk, NioSocketWrapper socketWrapper) \n 处理监听到的事件"] flow6["AbstractEndpoint#processSocket(SocketWrapperBase socketWrapper, SocketEvent event, boolean dispatch) \n 封装 SocketProcessor 多线程任务,提交到线程池处理"] flow1 --> flow2 flow2 --> |yes|flow3 flow3 --> flow4 flow4 --> flow5 flow5 --> flow6 flow6 --> flow2SocketProcessor 线程处理请求工作流程
flowchart TD flow1["SocketProcessor#doRun()"] flow2["ConnectionHandler#process(SocketWrapperBase socket, SocketEvent status)"] flow3["Http11Processor#process(SocketWrapperBase socketWrapper, SocketEvent status)"] flow4["CoyoteAdapter#service(Request req, Response res)"] subgraph subgraph1["Connector"] subgraph subgraph1_1["Service"] subgraph subgraph1_1_1["Engine"] subgraph subgraph1_1_1_1["Pipeline"] flow5["StandardEngineValve#invoke(Request request, Response response)"] end end end end subgraph subgraph2["Host"] subgraph subgraph 2_1["Pipeline"] flow6["StandardHostValve#invoke(Request request, Response response)"] end end subgraph subgraph3["Context"] subgraph subgraph 3_1["Pipeline"] flow7["StandardContextValve#invoke(Request request, Response response)"] end end subgraph subgraph4["Wrapper"] subgraph subgraph 4_1["Pipeline"] flow8["StandardWrapperValve#invoke(Request request, Response response)"] end end flow9["FilterChain#doFilter(ServletRequest request, ServletResponse response)"] flow10["DispatcherServlet#service(ServletRequest req, ServletResponse res)"] flow11["RequestMappingHandlerAdapter#handle(HttpServletRequest request, HttpServletResponse response, Object handler)"] flow12["ServletInvocableHandlerMethod#(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs)"] flow13["Controller 层,处理具体业务"] flow1 -->|NioSocketWrapper| flow2 flow2 -->|NioSocketWrapper| flow3 flow3 -->|Request, Response| flow4 flow4 -->|Request, Response| flow5 flow5 -->|Request, Response| flow6 flow6 -->|Request, Response| flow7 flow7 -->|Request, Response| flow8 flow8 -->|ServletRequest, ServletResponse| flow9 flow9 -->|ServletRequest, ServletResponse| flow10 flow10 -->|ServletRequest, ServletResponse, HandlerMethod| flow11 flow11 --> flow12 flow12 --> flow131.4. 配置参数优化
服务器配置:
CPU 核心 | 内存 |
---|---|
4 核 | 8G |
server:
tomcat:
threads:
max: 1000
min-spare: 200
accept-count: 1000
max-connections: 10000
标签:架构,Springboot,Tomcat,1.3,--,flow3,线程,class
From: https://www.cnblogs.com/jason207010/p/18211045