动态

详情 返回 返回

Springboot Tomcat 架構及參數優化 - 动态 详情

  • 1. Springboot Tomcat 架構及參數優化

    • 1.1. 版本説明
    • 1.2. SpringBoot Tomcat 架構分析

      • 1.2.1. Tomcat 核心組件類圖
      • 1.2.2. Tomcat 核心組件架構圖
    • 1.3. SpringBoot Tomcat 工作流程

      • 1.3.1. SpringBoot 初始化 Tomcat 流程
      • 1.3.2. Tomcat 啓動流程

        • 1.3.2.1. 初始化 ServerSocketChannel
        • 1.3.2.2. 初始化工作線程池
        • 1.3.2.3. 初始化限流組件 LimitLatch
      • 1.3.3. Acceptor 線程工作流程
      • 1.3.4. Poller 線程工作流程
      • SocketProcessor 線程處理請求工作流程
    • 1.4. 配置參數優化

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

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

Add a new 评论

Some HTML is okay.