Reactor 是一個面向高併發場景的分片對象池。它想回答一個很窄的問題:當分配器和 GC 成為熱點路徑的瓶頸時,池到底帶來了什麼,又拿走了什麼。
對象池常被當成“性能優化”的同義詞,但它本質上是在用內存保留和狀態管理,換取更少的分配和更穩定的尾延遲。這個項目把代價和收益一起放進設計裡。
為什麼需要池
在高併發、短生命週期對象的場景裡,分配頻率會直接轉化為 GC 壓力和 p99 抖動。標準庫的 sync.Pool 很適合臨時緩衝區,但它不提供容量上限、對象生命週期鉤子或可控的回收策略。
Reactor 面向的是另一類對象:初始化成本較高、可以被重置、需要明確借出和歸還,並且希望在過載時能看見等待和拒絕。
池的結構
- 每個 P 或 worker 擁有一個本地空閒鏈表,減少鎖競爭;
- 本地為空時,從其他分片偷取;
- 仍然沒有可用對象時,按上限創建新對象;
- 歸還時先 reset,再放回本地分片;
- 超過空閒上限的對象直接丟棄,避免內存無限增長。
每個池都實現四個生命週期鉤子:
type Pool[T any] interface {
New() T
Reset(T)
Validate(T) bool
Destroy(T)
}
Reset 負責清理狀態,Validate 決定對象是否還能複用,Destroy 處理連接、緩衝區等需要顯式釋放的資源。
生命週期與安全
池化最容易出問題的地方不是併發,而是對象仍然被外部引用時就被歸還。Reactor 在調試模式下記錄借出位置和歸還位置,並提供基礎的洩漏檢測。
池是有上限的。達到上限後,borrow 可以選擇等待一個短暫的時間窗口,或者直接失敗,把壓力交還給調用方。這個選擇必須顯式配置,而不是悄悄阻塞。
取捨與觀測
池不是免費的:
- 保留對象意味著更高的常駐內存;
- reset 不徹底會汙染下一次使用;
- 分片過多會增加偷取成本,分片過少會重新產生競爭;
- 對短生命週期對象,池化有時還不如直接分配。
因此項目把命中率、分配次數、borrow 等待時間、歸還失敗和 p50/p99 延遲放在同一組指標裡。沒有這些數據,就不應該假設池一定更快。
當前狀態
核心的 borrow/return、分片空閒鏈表、指標和基準測試已經成形。接下來會補充洩漏檢測、自適應容量,以及與 sync.Pool 的對照壓測。
池是延遲工具,不是內存優化。不能測量它,就不要池化。