Mongos連線模型探究
經常有同學會問, client/mongos/mongod之間的連線模型是怎樣的關係,一個客戶端連線對應多少個對後端mongod的連線。這個問題是有意義的,因為我們知道,client到mongod之間的連線,是 one-thread-per-connection的模式的,而且每個連線執行緒預設分配1MB記憶體,一千個連線就是1GB的記憶體; 而且活躍連線多了,核心態的執行緒切換引起的效能開銷又是一個讓人頭痛的問題。one-thread-per-connection的模型相當傳統(落後),該模型將執行緒切換/排程交給作業系統管理,帶來的結果就是:延遲不可控。不過mongos接入層的引入,較好的緩解了該問題,本文主要介紹mongos和mongod之間的連線池模型,以及調優引數項。
◆◆
連線模型
◆◆
mongos作為client和mongod之間的中間層,需要管理兩方面的連線
-
client對mongos的連線
-
mongos對mongod分片以及每個副本集的連線
mongos對client的連線模型
client對mongos的連線採用one-thread-per-connection的模式,listener執行緒負責accept到新的連線,每個連線新分配一個執行緒來處理,執行緒棧大小預設為1MB。

總結,mongos對客戶端的連線模型是採用one-thread-per-connection的經典方式。 另外,該方式有一個較為嚴重的問題:在活躍連線較多的場景下,會引起較多的執行緒切換,導致處理延時難以量化。
mongos對後端分片的連線模型
mongos對後端分片mongod的連線模型如下圖。 mongos採用ASIO網路框架,每個使用者請求通過網路實踐回撥/非同步狀態機的Reactor模型驅動。ReactorPool被劃分為N個ReactWorker,每個Worker對等的處理使用者請求。每個Worker中有M個連線池(M=後端所有mongod的個數)。如果後端有3分片,每個分片3副本,則每個Worker中管理9個連線池。

使用者請求被RoundRobin到ReactWorker中,如下程式碼所示:

◆◆
mongos連線池可調優引數
◆◆
mongos連線池可調優引數主要在 src/mongo/executor/connection pool.h 和 src/mongo/executor/connection pool.cpp 中。預設值為

連線池的細分
上面我們說過,mongos有一個ASIO的ReactorPool,每個Pool有有若干個連線池,每個連線池負責管理某個特定的mongod的連線。 每個連線池又分為 1. readyPool (管理空閒連線) 2. processingPool (管理在建立中/定期檢查健康狀態的連線) 3. checkoutPool (管理正在使用中的連線)
對於一個特定的連線,它在三個池中的狀態轉移關係如下圖。

-
minConnections 為預設為1,表示每個worker預設對每個mongod初始連線是1,且如果readyPool中連線過多,最終會將readyPool連線個數收斂到1
-
maxConnections 預設沒做限制,表示每個worker不限制對後端mongod的連線個數
-
refreshTimeout 和 refreshRequirement, 每個連線有最長IdleTime,大小為refreshRequirement,預設為5分鐘,超過這個值,如果當前連線個數大於minConnections,就會丟棄該連線,否則對該連線進行heartBeat後重新放回readyPool。refreshTimeout引數不重要,表示與mongod進行heartBeat的超時時間。
相關程式碼如下所示:

hostTimeout 如果該連線池長時間沒有處理任何請求,就將該連線池全部釋放掉。

ASIO ReactorWorkerPool大小
mongodb 提供taskExecutorPoolSize引數挑中mongos的ReactorPool的大小。使用者可以通過setParameter引數,在mongos的配置檔案中指定。該引數預設沒有指定,因此mongos預設的workerPool大小為cpu核心個數(cat /proc/cpuinfo)。

有價值的調優項
1. minConnection和 refreshRequirement
minConnection值預設為1, 舉個例子,如果在一個8核機器上預設配置部署mongos,則mongos對後端每個mongod有1X8=8個連線,如果是3副本,2分片,則總用有1X8X3X2=48個連線。如果有流量峰值,或後端mongod處理不過來,則會建立新的連線,峰值過去後,多餘的連線會在refreshRequrement指定的時間後釋放。 如果對業務平均響應和平均峰值間隔有個合理的預估,就可以有把握的調整這兩個值的大小。 舉個例子,如果兩分片,平均寫入響應時間在10ms,寫入TPS要求為1000。峰值間隔為5分鐘。則minConnection最好設定大於 1000/2(兩副本)/100(10ms)=5,從而防止冷啟動帶來的延遲開銷。refreshRequirement = 5X2 = 10分鐘,idle回收時間大於業務峰值間隔。從而避免峰值時建立新的連線。
2. maxConnection
這個值預設沒有做限制,這樣非常容易使mongos對mongod造成connection-flood。如果mongod請求處理超過客戶端超時。客戶端對mongos發起重試,mongos對mongod的請求還在checkoutPool中沒有回收,readyPool中不夠分配,只能建立新連線處理客戶端的重試,重試依然超時,如此往復導致對mongod的連線數暴增。 這個值建議通過最大QPS/TPS來估算。以寫為例: maxConnection = maxTPS / nShards / nMongos / taskExecutorPoolSize
3. taskExecutorPoolSize 與mongos部署模式
mongos預設不適合單機多部署,因為asioWorker的個數等於CPU核心個數。如果單機多部署,則需要考慮執行緒切換帶來的影響。比較合理的mongos單機部署多方式是使用cgroups或taskset將mongos binding到對應的核上。並且binding的核心個數 = taskExecutorPooSize
原文釋出時間為: 2018-11-22
本文作者:Mongoing中文社群
本文來自雲棲社群合作伙伴“ ofollow,noindex" target="_blank">Mongoing中文社群 ”,瞭解相關資訊可以關注“ Mongoing中文社群 ”。