對于 spring+tomcat 用戶每次發請求,tomcat 站在線程的角度是如何處理的 比如 bio nio apr 等情況
tomcat 配置文件中 maxThreads 的數量是相對于誰來說的?
以及 spring Controller 中的全局變量:各種bean 對于線程來說是共享的嗎?
一、Tomcat 處理請求的線程模型分析
在 Spring + Tomcat 架構中,Tomcat 根據配置的 I/O 模式(BIO、NIO、APR)采用不同的線程處理策略:
1. BIO(Blocking I/O)
- 線程模型:每個請求獨占一個線程,從連接建立到響應完成全程阻塞。適用于低并發場景,但線程資源消耗大。
- Acceptor 線程接收連接,Worker 線程處理請求,線程數由
maxThreads
控制。 - 缺點:高并發時線程數激增,性能急劇下降。
- Acceptor 線程接收連接,Worker 線程處理請求,線程數由
2. NIO(Non-blocking I/O)
- 線程模型:基于事件驅動,少量線程管理多個連接。核心組件包括:
- Acceptor:接收連接并注冊到 Poller。
- Poller:通過 Selector 監聽 I/O 事件,將就緒的請求交給 Worker 線程池處理。
- Worker 線程池:實際處理業務邏輯(如 Spring Controller 方法),線程數由
maxThreads
控制。 - 優點:適合高并發和長連接,資源利用率高。
3. APR(Apache Portable Runtime)
- 線程模型:基于本地庫(如 Epoll)優化 I/O 操作,線程模型與 NIO 類似,但通過 JNI 調用操作系統底層 API 提升性能。
- 默認最大連接數更高(如 8192),適合超高并發場景。
- 配置要求:需安裝 APR 庫和 Tomcat Native 組件。
二、maxThreads
參數的含義與配置
-
定義:
maxThreads
是 Tomcat 線程池中處理請求的最大線程數,直接影響并發處理能力。- 默認值:200(Spring Boot 默認)。
- 配置建議:
- 硬件相關:1 核 2G 建議 200,4 核 8G 建議 800。
- 業務類型:I/O 密集型(如數據庫操作)可適當增加;CPU 密集型需謹慎。
-
與
maxConnections
、acceptCount
的關系:maxConnections
:同時處理的連接數上限(NIO 默認 10000,APR 默認 8192)。acceptCount
:等待隊列長度,當連接數超過maxConnections
時,新請求進入隊列等待,隊列滿則拒絕請求。- 比喻:
maxThreads
是廚師數量,maxConnections
是餐桌數量,acceptCount
是排隊區容量。
三、Spring Controller 中的全局變量與線程安全
1. Bean 的作用域與線程安全性
- 默認作用域:Spring 的 Bean(如 Controller、Service)默認是單例(
singleton
),所有線程共享同一實例。- 無狀態 Bean:若 Bean 僅包含方法調用(無成員變量),則線程安全(方法局部變量在棧中隔離)。
- 有狀態 Bean:若包含可修改的成員變量(如
private int count
),多線程并發修改會導致數據競爭。
2. 線程安全問題的示例與解決方案
-
示例:
@RestController public class TestController {private int var = 0; // 共享變量,非線程安全@GetMapping("/test") public String test() { return "var: " + (++var); } }
- 多次請求會導致
var
遞增混亂。
- 多次請求會導致
-
解決方案:
- 作用域改為
prototype
:每次請求創建新實例,但需權衡性能(頻繁創建對象)。 - 使用
ThreadLocal
:線程私有變量存儲數據。 - 避免共享狀態:將數據存儲在方法參數或數據庫/緩存中。
- 作用域改為
3. 注入的 Bean 是否線程安全?
- 單例 Bean:如 Service、Dao,若自身無狀態(僅依賴注入其他無狀態 Bean),則線程安全。
- 有狀態 Bean:需通過同步機制(如鎖)或作用域調整保證安全。
最后
- Tomcat 線程模型選擇:優先使用 NIO 或 APR 模式以支持高并發,根據業務需求調整
maxThreads
和maxConnections
。 maxThreads
配置:需結合硬件資源和業務類型,避免過度配置導致 CPU 調度開銷。- Spring Bean 線程安全:默認單例 Bean 的成員變量需謹慎設計,推薦使用無狀態設計 其實就是我們平時的直接@bean 配置(無狀態)。