使用 MongoDB 時,可能會遇到因為 mongod 連接數用滿了,導致客戶端無法連接的問題。mongod的最大連接數通過 net.maxIncomingConnections 指定,默認值為1000000,相當於沒有限制,生產環境強烈建議根據實際需求配置,以避免客戶端誤用導致 mongod 負載過高。
Mongod 為什么需要限制連接數?
Mongod 的服務模型是每個網絡連接由一個單獨的線程來處理,每個線程配置了1MB 的棧空間,當網絡連接數太多時,過多的線程會導致上下文切換開銷變大,同時內存開銷也會上漲。
詳細的分析參考 雲數據庫MongoDB為什么需要限制連接數?
Driver如何使用?
MongoDB 各個語言的Driver 基本都會封裝包含一個 MongoClient 的對象(不同語言的 Driver 名字可能稍有不同),通常應用在使用時通過 MongoDB connection string URI 來構造一個全局的 MongoClient,然后在后續的請求中使用該全局對象來發送請求給Mongod。
應用使用的方式大致類似於
// 通常的用法
// global MongoClient object
mongoClient = new MongoClient("mongodb://root:****@host1:port1,host2:port2/admin?replicaSet=repl00& maxPoolSize=100");
// request1
db1 = mongoClient.getDatabase("db1");
coll1 = db1.getCollection("coll1");
coll1.find({...})
// request2
db2 = mongoClient.getDatabase("db2");
coll2 = db2.getCollection("coll2");
coll2.update({...})
// requestN
...
通常每個 MongoClient 會包含一個連接池,默認大小為100,也可以在構造 MongoClient 的時候通過 maxPoolSize 選項來指定。
一種典型的錯誤使用方式是,用戶為每個請求都構造一個 MongoClient,請求結束釋放 MongoClient(或根本沒釋放),這樣做問題是請求模型從長連接變成了短連接,每次短連接
都會增加『建立 tcp 連接 + mongodb鑒權』的開銷,並且並發的請求數會受限於連接數限制,極大的影響性能;另外如果 MongoClient 忘記釋放,會導致MongoClient 連接池里連接一直保持着,最終耗光所有的可用連接。
// 錯誤的用法
// request1
mongoClient = new MongoClient("mongodb://root:****@host1:port1,host2:port2/admin?replicaSet=repl00& maxPoolSize=100");
db1 = mongoClient.getDatabase("db1");
coll1 = db1.getCollection("coll1");
coll1.find({...});
mongoClient.close();
// request2
mongoClient = new MongoClient("mongodb://root:****@host1:port1,host2:port2/admin?replicaSet=repl00& maxPoolSize=100");
db2 = mongoClient.getDatabase("db2");
coll2 = db2.getCollection("coll2");
coll2.update({...});
MongoClient.close()
// requestN
...
MongoClient 連接池配置多大合適?
通常 MongoClient 使用默認100的連接池(具體默認值以 Driver 的文檔為准)都沒問題,當訪問同一個 Mongod 的源比較多時,則需要合理的規划連接池大小。
舉個例子,Mongod 的連接數限制為2000,應用業務上有40個服務進程可能同時訪問 這個Mongod,這時每個進程里的 MongoClient 的連接數則應該限制在 2000 / 40 = 50 以下 (連接復制集時,MongoClient 還要跟復制集的每個成員建立一條連接,用於監控復制集后端角色的變化情況)。
