1. Springboot Tomcat 架构及参数优化

1.1. 版本说明

构件版本
spring-boot2.7.18
tomcat-embed-core9.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

class VirtualThreadExecutor

class LimitLatch {
  -AtomicLong count
  -long limit
  +long countDown()
  +void countUpOrAwait()
}


class Handler {
<<Interface>>  
}

class ConnectionHandler~S~

class Poller

class Acceptor


class SocketProcessor
class SocketProcessorBase~S~


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~  ..|>  Runnable             
  1. Tomcat 线程池在 AbstractEndpoint 创建,server.tomcat.threads.max、server.tomcat.threads.min-spare 参数作用于此,用于指定最大、最小线程数,线程池其他参数默认值为:是否守护线程:是;线程优先级:5;空闲线程存活时间:60 秒;任务队列为:TaskQueue,容量为 Integer.MAX_VALUE
  2. LimitLatch 用于限制连接数量,基于 AQS 实现,server.tomcat.max-connections 参数作用于此,接受一个连接前判断是否达到最大连接数 limit,否则自旋等待直至成功并 count 加 1;关闭连接后 count 减 1
  3. Poller 线程不停从已连接的 socket 读取事件,最终封装成 SocketProcessorBase 交给 ThreadPoolExecutor 处理
  4. Acceptor 线程不停接收新的客户端连接,直至达到 server.tomcat.max-connections
  5. SocketProcessorBase 线程将请求经过层层传递最终给到DispatcherServlet,DispatcherServlet 再分派到对应的Spring Controller 中处理具体的业务逻辑

1.2.2. Tomcat 核心组件架构图

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 --> flow10

1.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 --> flow12
1.3.2.1. 初始化 ServerSocketChannel

核心源码:

serverSock = ServerSocketChannel.open();
InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
serverSock.bind(addr, getAcceptCount());
  1. 打开 ServerSocketChannel。
  2. 绑定端口,指定 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);
  1. TaskQueue 任务队列继承自 LinkedBlockingQueue,这里无法指定容量,默认容量为 Integer.MAX_VALUE,即无限大。预计未来将支持指定容量,详见 github issues
  2. TaskThreadFactory 线程工厂指定了线程名称前缀为 http-nio-端口-;线程为守护线程;线程优先级为默认值:5。
  3. 线程池核心线程数由 server.tomcat.threads.min-spare 配置参数指定,默认值为 10;线程池最大线程数由 server.tomcat.threads.max 配置参数指定,默认值为 200;空闲线程存活时间 60 秒。

TaskQueue 重写了 offer 方法,使得 Tomcat 线程池与 JDK 线程池创建线程的时机不一样,具体表现为:

  1. 如果线程池里的线程数量等于最大线程数,说明无法再创建新线程,任务加入队列中,等待空闲线程处理。
  2. 如果已提交的任务数小于等于线程池里的线程数量,说明有空闲线程,任务加入队列中,由空闲线程处理。
  3. 如果线程池里的线程数量小于最大线程数,任务无法加入队列,强制线程池新建线程去处理。
  4. 如果以上都不是,任务加入队列,等待空闲线程处理。

核心源码:

@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 --> flow2

1.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 --> flow2

SocketProcessor 线程处理请求工作流程

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 --> flow13

1.4. 配置参数优化

服务器配置:

CPU 核心内存
4 核8G
server:
  tomcat:
    threads:
      max: 1000
      min-spare: 200
    accept-count: 1000
    max-connections: 10000

Jason
4 声望0 粉丝