使用 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 还要跟复制集的每个成员建立一条连接,用于监控复制集后端角色的变化情况)。