连接

¥Connections

你可以使用 mongoose.connect() 方法连接到 MongoDB。

¥You can connect to MongoDB with the mongoose.connect() method.

mongoose.connect('mongodb://127.0.0.1:27017/myapp');

这是连接在默认端口 (27017) 上本地运行的 myapp 数据库所需的最低限度。对于本地 MongoDB 数据库,我们建议使用 127.0.0.1 而不是 localhost。这是因为 Node.js 18 及更高版本更喜欢 IPv6 地址,这意味着,在许多计算机上,Node.js 会将 localhost 解析为 IPv6 地址 ::1,并且 Mongoose 将无法连接,除非 mongodb 实例在启用 ipv6 的情况下运行。

¥This is the minimum needed to connect the myapp database running locally on the default port (27017). For local MongoDB databases, we recommend using 127.0.0.1 instead of localhost. That is because Node.js 18 and up prefer IPv6 addresses, which means, on many machines, Node.js will resolve localhost to the IPv6 address ::1 and Mongoose will be unable to connect, unless the mongodb instance is running with ipv6 enabled.

你还可以在 uri 中指定更多参数:

¥You can also specify several more parameters in the uri:

mongoose.connect('mongodb://username:password@host:port/database?options...');

有关详细信息,请参阅 mongodb 连接字符串规范

¥See the mongodb connection string spec for more details.

操作缓冲

¥Operation Buffering

Mongoose 让你可以立即开始使用模型,而无需等待 mongoose 建立与 MongoDB 的连接。

¥Mongoose lets you start using your models immediately, without waiting for mongoose to establish a connection to MongoDB.

mongoose.connect('mongodb://127.0.0.1:27017/myapp');
const MyModel = mongoose.model('Test', new Schema({ name: String }));
// Works
await MyModel.findOne();

这是因为 mongoose 在内部缓冲模型函数调用。这种缓冲很方便,但也是造成混乱的常见原因。如果你在没有连接的情况下使用模型,Mongoose 默认情况下不会抛出任何错误。

¥That's because mongoose buffers model function calls internally. This buffering is convenient, but also a common source of confusion. Mongoose will not throw any errors by default if you use a model without connecting.

const MyModel = mongoose.model('Test', new Schema({ name: String }));
const promise = MyModel.findOne();

setTimeout(function() {
  mongoose.connect('mongodb://127.0.0.1:27017/myapp');
}, 60000);

// Will just hang until mongoose successfully connects
await promise;

要禁用缓冲,请关闭 结构上的 bufferCommands 选项。如果你打开了 bufferCommands 并且连接挂起,请尝试关闭 bufferCommands 以查看是否未正确打开连接。你还可以全局禁用 bufferCommands

¥To disable buffering, turn off the bufferCommands option on your schema. If you have bufferCommands on and your connection is hanging, try turning bufferCommands off to see if you haven't opened a connection properly. You can also disable bufferCommands globally:

mongoose.set('bufferCommands', false);

请注意,如果你使用 autoCreate 选项.0,缓冲还负责等待 Mongoose 创建集合。如果禁用缓冲,还应该禁用 autoCreate 选项并使用 createCollection() 创建 上限集合带有排序规则的集合

¥Note that buffering is also responsible for waiting until Mongoose creates collections if you use the autoCreate option. If you disable buffering, you should also disable the autoCreate option and use createCollection() to create capped collections or collections with collations.

const schema = new Schema({
  name: String
}, {
  capped: { size: 1024 },
  bufferCommands: false,
  autoCreate: false // disable `autoCreate` since `bufferCommands` is false
});

const Model = mongoose.model('Test', schema);
// Explicitly create the collection before using it
// so the collection is capped.
await Model.createCollection();

错误处理

¥Error Handling

Mongoose 连接可能会发生两类错误。

¥There are two classes of errors that can occur with a Mongoose connection.

  • 初始连接时出错:如果初始连接失败,Mongoose 将发出 'error' 事件,并且返回的 promise mongoose.connect() 将被拒绝。然而,Mongoose 不会自动尝试重新连接。

    ¥Error on initial connection: If initial connection fails, Mongoose will emit an 'error' event and the promise mongoose.connect() returns will reject. However, Mongoose will not automatically try to reconnect.

  • 建立初始连接后出错:Mongoose 将尝试重新连接,并且会发出 'error' 事件。

    ¥Error after initial connection was established: Mongoose will attempt to reconnect, and it will emit an 'error' event.

要处理初始连接错误,你应该将 .catch()try/catch 与 async/await 结合使用。

¥To handle initial connection errors, you should use .catch() or try/catch with async/await.

mongoose.connect('mongodb://127.0.0.1:27017/test').
  catch(error => handleError(error));

// Or:
try {
  await mongoose.connect('mongodb://127.0.0.1:27017/test');
} catch (error) {
  handleError(error);
}

要在建立初始连接后处理错误,你应该监听连接上的错误事件。但是,你仍然需要处理初始连接错误,如上所示。

¥To handle errors after initial connection was established, you should listen for error events on the connection. However, you still need to handle initial connection errors as shown above.

mongoose.connection.on('error', err => {
  logError(err);
});

请注意,如果 Mongoose 失去与 MongoDB 的连接,它不一定会发出 'error' 事件。当 Mongoose 与 MongoDB 断开连接时,你应该监听 disconnected 事件来报告。

¥Note that Mongoose does not necessarily emit an 'error' event if it loses connectivity to MongoDB. You should listen to the disconnected event to report when Mongoose is disconnected from MongoDB.

选项

¥Options

connect 方法还接受 options 对象,该对象将传递给底层 MongoDB 驱动程序。

¥The connect method also accepts an options object which will be passed on to the underlying MongoDB driver.

mongoose.connect(uri, options);

完整的选项列表可以在 MongoClientOptions 的 MongoDB Node.js 驱动程序文档 上找到。Mongoose 将选项传递给驱动程序而不进行修改,并对一些例外情况进行取模,如下所述。

¥A full list of options can be found on the MongoDB Node.js driver docs for MongoClientOptions. Mongoose passes options to the driver without modification, modulo a few exceptions that are explained below.

  • bufferCommands - 这是一个特定于 Mongoose 的选项(未传递给 MongoDB 驱动程序),用于禁用 Mongoose 的缓冲机制

    ¥bufferCommands - This is a mongoose-specific option (not passed to the MongoDB driver) that disables Mongoose's buffering mechanism

  • user/pass - 用于身份验证的用户名和密码。这些选项是 Mongoose 特定的,它们相当于 MongoDB 驱动程序的 auth.usernameauth.password 选项。

    ¥user/pass - The username and password for authentication. These options are Mongoose-specific, they are equivalent to the MongoDB driver's auth.username and auth.password options.

  • autoIndex - 默认情况下,mongoose 在连接时会自动构建在你的架构中定义的索引。这对于开发来说非常有用,但对于大型生产部署来说并不理想,因为索引构建可能会导致性能下降。如果将 autoIndex 设置为 false,mongoose 将不会自动为与此连接关联的任何模型构建索引。

    ¥autoIndex - By default, mongoose will automatically build indexes defined in your schema when it connects. This is great for development, but not ideal for large production deployments, because index builds can cause performance degradation. If you set autoIndex to false, mongoose will not automatically build indexes for any model associated with this connection.

  • dbName - 指定要连接到的数据库并覆盖连接字符串中指定的任何数据库。如果你无法像 一些 mongodb+srv 语法连接 那样在连接字符串中指定默认数据库,这会很有用。

    ¥dbName - Specifies which database to connect to and overrides any database specified in the connection string. This is useful if you are unable to specify a default database in the connection string like with some mongodb+srv syntax connections.

以下是一些对于调整 Mongoose 很重要的选项。

¥Below are some of the options that are important for tuning Mongoose.

  • promiseLibrary - 设置 底层驱动程序的 promise 库

    ¥promiseLibrary - Sets the underlying driver's promise library.

  • maxPoolSize - MongoDB 驱动程序为此连接保持打开的最大套接字数。默认情况下,maxPoolSize 为 100。请记住,MongoDB 一次只允许每个套接字执行一个操作,因此,如果你发现有一些缓慢的查询阻碍了更快的查询的继续,你可能需要增加此值。参见 MongoDB 和 Node.js 中的慢车。如果遇到 连接限制,你可能需要减少 maxPoolSize

    ¥maxPoolSize - The maximum number of sockets the MongoDB driver will keep open for this connection. By default, maxPoolSize is 100. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See Slow Trains in MongoDB and Node.js. You may want to decrease maxPoolSize if you are running into connection limits.

  • minPoolSize - MongoDB 驱动程序为此连接保持打开的最小套接字数。MongoDB 驱动程序可能会关闭一段时间不活动的套接字。如果你希望你的应用经历较长的空闲时间,并希望确保你的套接字保持打开状态以避免活动开始时缓慢运行,你可能需要增加 minPoolSize

    ¥minPoolSize - The minimum number of sockets the MongoDB driver will keep open for this connection. The MongoDB driver may close sockets that have been inactive for some time. You may want to increase minPoolSize if you expect your app to go through long idle times and want to make sure your sockets stay open to avoid slow trains when activity picks up.

  • socketTimeoutMS - 由于初始连接后不活动,MongoDB 驱动程序在终止套接字之前将等待多长时间。套接字可能由于没有活动或长时间运行的操作而处于非活动状态。socketTimeoutMS 默认为 0,这意味着 Node.js 不会因不活动而使套接字超时。MongoDB 驱动程序成功完成后,此选项将传递给 Node.js socket#setTimeout() 函数

    ¥socketTimeoutMS - How long the MongoDB driver will wait before killing a socket due to inactivity after initial connection. A socket may be inactive because of either no activity or a long-running operation. socketTimeoutMS defaults to 0, which means Node.js will not time out the socket due to inactivity. This option is passed to Node.js socket#setTimeout() function after the MongoDB driver successfully completes.

  • family - 是使用 IPv4 还是 IPv6 进行连接。该选项传递给 Node.js 的 dns.lookup() 函数。如果不指定此选项,MongoDB 驱动程序将首先尝试 IPv6,如果 IPv6 失败,则再尝试 IPv4。如果你的 mongoose.connect(uri) 调用需要很长时间,请尝试 mongoose.connect(uri, { family: 4 })

    ¥family - Whether to connect using IPv4 or IPv6. This option passed to Node.js' dns.lookup() function. If you don't specify this option, the MongoDB driver will try IPv6 first and then IPv4 if IPv6 fails. If your mongoose.connect(uri) call takes a long time, try mongoose.connect(uri, { family: 4 })

  • authSource - 使用 userpass 进行身份验证时使用的数据库。在 MongoDB 中,用户的范围仅限于数据库。如果你遇到意外的登录失败,你可能需要设置此选项。

    ¥authSource - The database to use when authenticating with user and pass. In MongoDB, users are scoped to a database. If you are getting an unexpected login failure, you may need to set this option.

  • serverSelectionTimeoutMS - MongoDB 驱动程序将尝试找到一个服务器来发送任何给定的操作,并持续重试 serverSelectionTimeoutMS 毫秒。如果未设置,MongoDB 驱动程序默认使用 30000(30 秒)。

    ¥serverSelectionTimeoutMS - The MongoDB driver will try to find a server to send any given operation to, and keep retrying for serverSelectionTimeoutMS milliseconds. If not set, the MongoDB driver defaults to using 30000 (30 seconds).

  • heartbeatFrequencyMS - MongoDB 驱动程序每隔 heartbeatFrequencyMS 发送一次心跳来检查连接状态。心跳受 serverSelectionTimeoutMS 约束,因此 MongoDB 驱动程序默认会重试失败的心跳最多 30 秒。Mongoose 仅在心跳失败后发出 'disconnected' 事件,因此你可能需要减少此设置以减少服务器停机和 Mongoose 发出 'disconnected' 之间的时间。我们建议你不要将此设置设置为低于 1000,过多的心跳会导致性能下降。

    ¥heartbeatFrequencyMS - The MongoDB driver sends a heartbeat every heartbeatFrequencyMS to check on the status of the connection. A heartbeat is subject to serverSelectionTimeoutMS, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a 'disconnected' event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits 'disconnected'. We recommend you do not set this setting below 1000, too many heartbeats can lead to performance degradation.

serverSelectionTimeoutMS

serverSelectionTimeoutMS 选项极其重要:它控制 MongoDB Node.js 驱动程序在出错之前尝试重试任何操作的时间。这包括初始连接(如 await mongoose.connect())以及向 MongoDB 发出请求的任何操作(如 save()find())。

¥The serverSelectionTimeoutMS option is extremely important: it controls how long the MongoDB Node.js driver will attempt to retry any operation before erroring out. This includes initial connection, like await mongoose.connect(), as well as any operations that make requests to MongoDB, like save() or find().

默认情况下,serverSelectionTimeoutMS 为 30000(30 秒)。这意味着,例如,如果你在独立 MongoDB 服务器关闭时调用 mongoose.connect(),则 mongoose.connect() 调用只会在 30 秒后抛出错误。

¥By default, serverSelectionTimeoutMS is 30000 (30 seconds). This means that, for example, if you call mongoose.connect() when your standalone MongoDB server is down, your mongoose.connect() call will only throw an error after 30 seconds.

// Throws an error "getaddrinfo ENOTFOUND doesnt.exist" after 30 seconds
await mongoose.connect('mongodb://doesnt.exist:27017/test');

同样,如果你的独立 MongoDB 服务器在初始连接后出现故障,任何 find()save() 调用都将在 30 秒后出错,除非你的 MongoDB 服务器重新启动。

¥Similarly, if your standalone MongoDB server goes down after initial connection, any find() or save() calls will error out after 30 seconds, unless your MongoDB server is restarted.

虽然 30 秒看起来很长,但 serverSelectionTimeoutMS 意味着你在 副本集故障转移 期间不太可能看到任何中断。如果你丢失了主副本集,MongoDB Node 驱动程序将确保你在副本集选举期间发送的任何操作最终都会执行,假设副本集选举花费的时间少于 serverSelectionTimeoutMS

¥While 30 seconds seems like a long time, serverSelectionTimeoutMS means you're unlikely to see any interruptions during a replica set failover. If you lose your replica set primary, the MongoDB Node driver will ensure that any operations you send during the replica set election will eventually execute, assuming that the replica set election takes less than serverSelectionTimeoutMS.

要更快地获得有关失败连接的反馈,你可以将 serverSelectionTimeoutMS 减少到 5000,如下所示。我们不建议减少 serverSelectionTimeoutMS,除非你运行的是独立的 MongoDB 服务器而不是副本集,或者除非你使用像 AWS Lambda 这样的无服务器运行时。

¥To get faster feedback on failed connections, you can reduce serverSelectionTimeoutMS to 5000 as follows. We don't recommend reducing serverSelectionTimeoutMS unless you are running a standalone MongoDB server rather than a replica set, or unless you are using a serverless runtime like AWS Lambda.

mongoose.connect(uri, {
  serverSelectionTimeoutMS: 5000
});

无法针对 mongoose.connect() 与查询独立调整 serverSelectionTimeoutMS。如果你想减少查询和其他操作的 serverSelectionTimeoutMS,但仍重试 mongoose.connect() 时间更长,则你有责任使用 for 循环或 像预重试这样的工具 自己重试 connect() 调用。

¥There is no way to tune serverSelectionTimeoutMS independently for mongoose.connect() vs for queries. If you want to reduce serverSelectionTimeoutMS for queries and other operations, but still retry mongoose.connect() for longer, you are responsible for retrying the connect() calls yourself using a for loop or a tool like p-retry.

const serverSelectionTimeoutMS = 5000;

// Prints "Failed 0", "Failed 1", "Failed 2" and then throws an
// error. Exits after approximately 15 seconds.
for (let i = 0; i < 3; ++i) {
  try {
    await mongoose.connect('mongodb://doesnt.exist:27017/test', {
      serverSelectionTimeoutMS
    });
    break;
  } catch (err) {
    console.log('Failed', i);
    if (i >= 2) {
      throw err;
    }
  }
}

回调

¥Callback

connect() 函数还接受回调参数并返回 promise

¥The connect() function also accepts a callback parameter and returns a promise.

mongoose.connect(uri, options, function(error) {
  // Check error in initial connection. There is no 2nd param to the callback.
});

// Or using promises
mongoose.connect(uri, options).then(
  () => { /** ready to use. The `mongoose.connect()` promise resolves to mongoose instance. */ },
  err => { /** handle initial connection error */ }
);

连接字符串选项

¥Connection String Options

你还可以在连接字符串中将驱动程序选项指定为 URI 的 查询字符串中的参数 部分。这仅适用于传递给 MongoDB 驱动程序的选项。你无法在查询字符串中设置 Mongoose 特定选项,例如 bufferCommands

¥You can also specify driver options in your connection string as parameters in the query string portion of the URI. This only applies to options passed to the MongoDB driver. You can't set Mongoose-specific options like bufferCommands in the query string.

mongoose.connect('mongodb://127.0.0.1:27017/test?socketTimeoutMS=1000&bufferCommands=false&authSource=otherdb');
// The above is equivalent to:
mongoose.connect('mongodb://127.0.0.1:27017/test', {
  socketTimeoutMS: 1000
  // Note that mongoose will **not** pull `bufferCommands` from the query string
});

将选项放入查询字符串的缺点是查询字符串选项更难阅读。优点是你只需要一个配置选项,URI,而不是 socketTimeoutMS 等的单独选项。最佳实践是将开发和生产之间可能不同的选项(例如 replicaSetssl)放在连接字符串中,并且选项 在选项对象中应该保持不变,例如 socketTimeoutMSmaxPoolSize

¥The disadvantage of putting options in the query string is that query string options are harder to read. The advantage is that you only need a single configuration option, the URI, rather than separate options for socketTimeoutMS, etc. Best practice is to put options that likely differ between development and production, like replicaSet or ssl, in the connection string, and options that should remain constant, like socketTimeoutMS or maxPoolSize, in the options object.

MongoDB 文档有 支持的连接字符串选项 的完整列表。以下是在连接字符串中设置的一些选项通常很有用,因为它们与主机名和身份验证信息密切相关。

¥The MongoDB docs have a full list of supported connection string options. Below are some options that are often useful to set in the connection string because they are closely associated with the hostname and authentication information.

  • authSource - 使用 userpass 进行身份验证时使用的数据库。在 MongoDB 中,用户的范围仅限于数据库。如果你遇到意外的登录失败,你可能需要设置此选项。

    ¥authSource - The database to use when authenticating with user and pass. In MongoDB, users are scoped to a database. If you are getting an unexpected login failure, you may need to set this option.

  • family - 是使用 IPv4 还是 IPv6 进行连接。该选项传递给 Node.js 的 dns.lookup() 函数。如果不指定此选项,MongoDB 驱动程序将首先尝试 IPv6,如果 IPv6 失败,则再尝试 IPv4。如果你的 mongoose.connect(uri) 调用需要很长时间,请尝试 mongoose.connect(uri, { family: 4 })

    ¥family - Whether to connect using IPv4 or IPv6. This option passed to Node.js' dns.lookup() function. If you don't specify this option, the MongoDB driver will try IPv6 first and then IPv4 if IPv6 fails. If your mongoose.connect(uri) call takes a long time, try mongoose.connect(uri, { family: 4 })

连接事件

¥Connection Events

连接继承自 Node.js 的 EventEmitter,并在连接发生问题时触发事件,例如失去与 MongoDB 服务器的连接。下面是连接可能发出的事件列表。

¥Connections inherit from Node.js' EventEmitter class, and emit events when something happens to the connection, like losing connectivity to the MongoDB server. Below is a list of events that a connection may emit.

  • connecting:当 Mongoose 开始与 MongoDB 服务器建立初始连接时发出

    ¥connecting: Emitted when Mongoose starts making its initial connection to the MongoDB server

  • connected:当 Mongoose 成功与 MongoDB 服务器建立初始连接时,或者当 Mongoose 在失去连接后重新连接时发出。如果 Mongoose 失去连接,可能会多次发出。

    ¥connected: Emitted when Mongoose successfully makes its initial connection to the MongoDB server, or when Mongoose reconnects after losing connectivity. May be emitted multiple times if Mongoose loses connectivity.

  • open:在所有该连接的模型上执行 'connected'onOpen 后发出。如果 Mongoose 失去连接,可能会多次发出。

    ¥open: Emitted after 'connected' and onOpen is executed on all of this connection's models. May be emitted multiple times if Mongoose loses connectivity.

  • disconnecting:你的应用调用 Connection#close() 来断开与 MongoDB 的连接。这包括调用 mongoose.disconnect(),后者在所有连接上调用 close()

    ¥disconnecting: Your app called Connection#close() to disconnect from MongoDB. This includes calling mongoose.disconnect(), which calls close() on all connections.

  • disconnected:当 Mongoose 失去与 MongoDB 服务器的连接时发出。此事件可能是由于你的代码显式关闭连接、数据库服务器崩溃或网络连接问题造成的。

    ¥disconnected: Emitted when Mongoose lost connection to the MongoDB server. This event may be due to your code explicitly closing the connection, the database server crashing, or network connectivity issues.

  • closeConnection#close() 成功关闭连接后发出。如果你调用 conn.close(),你将同时收到 'disconnected' 事件和 'close' 事件。

    ¥close: Emitted after Connection#close() successfully closes the connection. If you call conn.close(), you'll get both a 'disconnected' event and a 'close' event.

  • reconnected:如果 Mongoose 失去与 MongoDB 的连接并成功重新连接,则发出。Mongoose 在失去与数据库的连接时尝试 自动重新连接

    ¥reconnected: Emitted if Mongoose lost connectivity to MongoDB and successfully reconnected. Mongoose attempts to automatically reconnect when it loses connection to the database.

  • error:如果连接上发生错误,例如由于格式错误的数据或大于 16MB 的有效负载而导致 parseError,则发出该错误。

    ¥error: Emitted if an error occurs on a connection, like a parseError due to malformed data or a payload larger than 16MB.

当你连接到单个 MongoDB 服务器("standalone")时,如果 Mongoose 与独立服务器断开连接,则会发出 disconnected;如果成功连接到独立服务器,则会发出 connected。在 副本集 中,如果 Mongoose 失去与主副本集的连接,则 Mongoose 将发出 disconnected;如果设法重新连接到主副本集,则将发出 connected

¥When you're connecting to a single MongoDB server (a "standalone"), Mongoose will emit disconnected if it gets disconnected from the standalone server, and connected if it successfully connects to the standalone. In a replica set, Mongoose will emit disconnected if it loses connectivity to the replica set primary, and connected if it manages to reconnect to the replica set primary.

如果你使用的是 mongoose.connect(),你可以使用以下命令来监听上述事件:

¥If you are using mongoose.connect(), you can use the following to listen to the above events:

mongoose.connection.on('connected', () => console.log('connected'));
mongoose.connection.on('open', () => console.log('open'));
mongoose.connection.on('disconnected', () => console.log('disconnected'));
mongoose.connection.on('reconnected', () => console.log('reconnected'));
mongoose.connection.on('disconnecting', () => console.log('disconnecting'));
mongoose.connection.on('close', () => console.log('close'));

mongoose.connect('mongodb://127.0.0.1:27017/mongoose_test');

对于 mongoose.createConnection(),请改用以下内容:

¥With mongoose.createConnection(), use the following instead:

const conn = mongoose.createConnection('mongodb://127.0.0.1:27017/mongoose_test');

conn.on('connected', () => console.log('connected'));
conn.on('open', () => console.log('open'));
conn.on('disconnected', () => console.log('disconnected'));
conn.on('reconnected', () => console.log('reconnected'));
conn.on('disconnecting', () => console.log('disconnecting'));
conn.on('close', () => console.log('close'));

关于 keepAlive 的注意事项

¥A note about keepAlive

在 Mongoose 5.2.0 之前,你需要启用 keepAlive 选项来启动 TCP 保活 以防止 "connection closed" 错误。然而,从 Mongoose 5.2.0 开始,keepAlive 默认为 true,从 Mongoose 7.2.0 开始,keepAlive 已被弃用。请从你的 Mongoose 连接中删除 keepAlivekeepAliveInitialDelay 选项。

¥Before Mongoose 5.2.0, you needed to enable the keepAlive option to initiate TCP keepalive to prevent "connection closed" errors. However, keepAlive has been true by default since Mongoose 5.2.0, and the keepAlive is deprecated as of Mongoose 7.2.0. Please remove keepAlive and keepAliveInitialDelay options from your Mongoose connections.

副本集连接

¥Replica Set Connections

要连接到副本集,你需要传递要连接的主机的逗号分隔列表,而不是单个主机。

¥To connect to a replica set you pass a comma delimited list of hosts to connect to rather than a single host.

mongoose.connect('mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]' [, options]);

例如:

¥For example:

mongoose.connect('mongodb://user:pw@host1.com:27017,host2.com:27017,host3.com:27017/testdb');

要连接到单节点副本集,请指定 replicaSet 选项。

¥To connect to a single node replica set, specify the replicaSet option.

mongoose.connect('mongodb://host1:port1/?replicaSet=rsName');

服务器选择

¥Server Selection

底层 MongoDB 驱动程序使用名为 服务器选择 的进程连接到 MongoDB 并向 MongoDB 发送操作。如果 MongoDB 驱动程序在 serverSelectionTimeoutMS 之后找不到服务器来发送操作,你将收到以下错误:

¥The underlying MongoDB driver uses a process known as server selection to connect to MongoDB and send operations to MongoDB. If the MongoDB driver can't find a server to send an operation to after serverSelectionTimeoutMS, you'll get the below error:

MongoTimeoutError: Server selection timed out after 30000 ms

你可以使用 serverSelectionTimeoutMS 选项到 mongoose.connect() 配置超时:

¥You can configure the timeout using the serverSelectionTimeoutMS option to mongoose.connect():

mongoose.connect(uri, {
  serverSelectionTimeoutMS: 5000 // Timeout after 5s instead of 30s
});

MongoTimeoutError 具有 reason 属性,用于解释服务器选择超时的原因。例如,如果你使用不正确的密码连接到独立服务器,则 reason 将包含 "认证失败" 错误。

¥A MongoTimeoutError has a reason property that explains why server selection timed out. For example, if you're connecting to a standalone server with an incorrect password, reason will contain an "Authentication failed" error.

const mongoose = require('mongoose');

const uri = 'mongodb+srv://username:badpw@cluster0-OMITTED.mongodb.net/' +
  'test?retryWrites=true&w=majority';
// Prints "MongoServerError: bad auth Authentication failed."
mongoose.connect(uri, {
  serverSelectionTimeoutMS: 5000
}).catch(err => console.log(err.reason));

副本集主机名

¥Replica Set Host Names

MongoDB 副本集依赖于能够可靠地找出每个成员的域名。在 Linux 和 OSX 上,MongoDB 服务器使用 hostname 命令 的输出来确定要向副本集报告的域名。如果你连接到在将 hostname 报告为 localhost 的计算机上运行的远程 MongoDB 副本集,这可能会导致令人困惑的错误:

¥MongoDB replica sets rely on being able to reliably figure out the domain name for each member.\ On Linux and OSX, the MongoDB server uses the output of the hostname command to figure out the domain name to report to the replica set. This can cause confusing errors if you're connecting to a remote MongoDB replica set running on a machine that reports its hostname as localhost:

// Can get this error even if your connection string doesn't include
// `localhost` if `rs.conf()` reports that one replica set member has
// `localhost` as its host name.
MongooseServerSelectionError: connect ECONNREFUSED localhost:27017

如果你遇到类似的错误,请使用 mongo shell 连接到副本集并运行 rs.conf() 命令来检查每个副本集成员的主机名。跟随 本页有关更改副本集成员主机名的说明

¥If you're experiencing a similar error, connect to the replica set using the mongo shell and run the rs.conf() command to check the host names of each replica set member. Follow this page's instructions to change a replica set member's host name.

你还可以检查 MongooseServerSelectionErrorreason.servers 属性,以查看 MongoDB Node 驱动程序认为你的副本集的状态是什么。reason.servers 属性包含 map 个服务器描述。

¥You can also check the reason.servers property of MongooseServerSelectionError to see what the MongoDB Node driver thinks the state of your replica set is. The reason.servers property contains a map of server descriptions.

if (err.name === 'MongooseServerSelectionError') {
  // Contains a Map describing the state of your replica set. For example:
  // Map(1) {
  //   'localhost:27017' => ServerDescription {
  //     address: 'localhost:27017',
  //     type: 'Unknown',
  //     ...
  //   }
  // }
  console.log(err.reason.servers);
}

多 mongos 支持

¥Multi-mongos support

你还可以连接到多个 mongos 实例,以在分片集群中实现高可用性。你在 mongoose 5.x 中执行 不需要传递任何特殊选项来连接多个 mongos

¥You can also connect to multiple mongos instances for high availability in a sharded cluster. You do not need to pass any special options to connect to multiple mongos in mongoose 5.x.

// Connect to 2 mongos servers
mongoose.connect('mongodb://mongosA:27501,mongosB:27501', cb);

多个连接

¥Multiple connections

到目前为止,我们已经了解了如何使用 Mongoose 的默认连接来连接 MongoDB。当你调用 mongoose.connect() 时,Mongoose 会创建一个默认连接。你可以使用 mongoose.connection 访问默认连接。

¥So far we've seen how to connect to MongoDB using Mongoose's default connection. Mongoose creates a default connection when you call mongoose.connect(). You can access the default connection using mongoose.connection.

由于多种原因,你可能需要多个 MongoDB 连接。原因之一是你是否有多个数据库或多个 MongoDB 集群。另一个原因是围绕 慢车 进行工作。mongoose.createConnection() 函数采用与 mongoose.connect() 相同的参数并返回一个新连接。

¥You may need multiple connections to MongoDB for several reasons. One reason is if you have multiple databases or multiple MongoDB clusters. Another reason is to work around slow trains. The mongoose.createConnection() function takes the same arguments as mongoose.connect() and returns a new connection.

const conn = mongoose.createConnection('mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]', options);

然后使用该 connection 对象创建和检索 models。模型的范围始终限于单个连接。

¥This connection object is then used to create and retrieve models. Models are always scoped to a single connection.

const UserModel = conn.model('User', userSchema);

createConnection() 函数返回一个连接实例,而不是一个 promise。如果你想使用 await 确保 Mongoose 成功连接到 MongoDB,请使用 asPromise() 功能

¥The createConnection() function returns a connection instance, not a promise. If you want to use await to make sure Mongoose successfully connects to MongoDB, use the asPromise() function:

// `asPromise()` returns a promise that resolves to the connection
// once the connection succeeds, or rejects if connection failed.
const conn = await mongoose.createConnection(connectionString).asPromise();

如果你使用多个连接,则应确保导出架构,而不是模型。从文件导出模型称为导出模型结构。导出模型结构受到限制,因为你只能使用一个连接。

¥If you use multiple connections, you should make sure you export schemas, not models. Exporting a model from a file is called the export model pattern. The export model pattern is limited because you can only use one connection.

const userSchema = new Schema({ name: String, email: String });

// The alternative to the export model pattern is the export schema pattern.
module.exports = userSchema;

// Because if you export a model as shown below, the model will be scoped
// to Mongoose's default connection.
// module.exports = mongoose.model('User', userSchema);

如果你使用导出结构结构,你仍然需要在某处创建模型。有两种常见的结构。第一个是创建一个实例化新连接并在该连接上注册所有模型的函数。使用此模式,你还可以向依赖注入器或另一个 控制反转 (IOC) 模式 注册连接。

¥If you use the export schema pattern, you still need to create models somewhere. There are two common patterns. The first is to create a function that instantiates a new connection and registers all models on that connection. With this pattern, you may also register connections with a dependency injector or another inversion of control (IOC) pattern.

const mongoose = require('mongoose');

module.exports = function connectionFactory() {
  const conn = mongoose.createConnection(process.env.MONGODB_URI);

  conn.model('User', require('../schemas/user'));
  conn.model('PageView', require('../schemas/pageView'));

  return conn;
};

导出创建新连接的函数是最灵活的模式。但是,该模式可能会使你从路由处理程序或业务逻辑所在的任何地方访问连接变得棘手。另一种模式是导出连接并在文件的顶层范围内注册连接上的模型,如下所示。

¥Exporting a function that creates a new connection is the most flexible pattern. However, that pattern can make it tricky to get access to your connection from your route handlers or wherever your business logic is. An alternative pattern is to export a connection and register the models on the connection in the file's top-level scope as follows.

// connections/index.js
const mongoose = require('mongoose');

const conn = mongoose.createConnection(process.env.MONGODB_URI);
conn.model('User', require('../schemas/user'));

module.exports = conn;

如果你想为 Web API 后端和移动 API 后端创建单独的连接,则可以为每个连接创建单独的文件,例如 connections/web.jsconnections/mobile.js。然后,你的业务逻辑可以 require()import 所需的连接。

¥You can create separate files for each connection, like connections/web.js and connections/mobile.js if you want to create separate connections for your web API backend and your mobile API backend. Your business logic can then require() or import the connection it needs.

连接池

¥Connection Pools

每个 connection,无论是使用 mongoose.connect 还是 mongoose.createConnection 创建,都由内部可配置连接池支持,默认最大大小为 100。使用你的连接选项调整池大小:

¥Each connection, whether created with mongoose.connect or mongoose.createConnection are all backed by an internal configurable connection pool defaulting to a maximum size of 100. Adjust the pool size using your connection options:

// With object options
mongoose.createConnection(uri, { maxPoolSize: 10 });

// With connection string options
const uri = 'mongodb://127.0.0.1:27017/test?maxPoolSize=10';
mongoose.createConnection(uri);

连接池大小很重要,因为 MongoDB 目前每个套接字只能处理一个操作。因此 maxPoolSize 起到了并发操作数量的上限的作用。

¥The connection pool size is important because MongoDB currently can only process one operation per socket. So maxPoolSize functions as a cap on the number of concurrent operations.

多租户连接

¥Multi Tenant Connections

在 Mongoose 的上下文中,多租户结构通常意味着多个不同的客户端通过单个 Mongoose 应用与 MongoDB 通信的情况。这通常意味着每个客户端通过单个 Mongoose 应用进行查询并执行更新,但在同一 MongoDB 集群中拥有不同的 MongoDB 数据库。

¥In the context of Mongoose, a multi-tenant architecture typically means a case where multiple different clients talk to MongoDB through a single Mongoose application. This typically means each client makes queries and executes updates through a single Mongoose application, but has a distinct MongoDB database within the same MongoDB cluster.

我们推荐阅读 这篇关于 Mongoose 多租户的文章;它很好地描述了我们如何定义多租户,并对我们推荐的结构进行了更详细的概述。

¥We recommend reading this article about multi-tenancy with Mongoose; it has a good description of how we define multi-tenancy and a more detailed overview of our recommended patterns.

对于 Mongoose 中的多租户,我们推荐两种结构:

¥There are two patterns we recommend for multi-tenancy in Mongoose:

  1. 维护一个连接池,使用 Connection.prototype.useDb() method 在租户之间切换。

    ¥Maintain one connection pool, switch between tenants using the Connection.prototype.useDb() method.

  2. 为每个租户维护一个单独的连接池,将连接存储在映射或 POJO 中。

    ¥Maintain a separate connection pool per tenant, store connections in a map or POJO.

以下是结构(1)的示例。对于租户数量较少或每个租户的工作负载较轻(大约每秒 < 1 个请求,所有请求占用 < 10 毫秒的数据库处理时间)的情况,我们建议使用模式 (1)。结构(1)在生产中实现起来更简单,管理也更简单,因为只有 1 个连接池。但是,在高负载下,你可能会遇到一些租户 '运营会拖慢其他租户的速度' 由于 慢车 而无法运行的问题。

¥The following is an example of pattern (1). We recommend pattern (1) for cases where you have a small number of tenants, or if each individual tenant's workload is light (approximately < 1 request per second, all requests take < 10ms of database processing time). Pattern (1) is simpler to implement and simpler to manage in production, because there is only 1 connection pool. But, under high load, you will likely run into issues where some tenants' operations slow down other tenants' operations due to slow trains.

const express = require('express');
const mongoose = require('mongoose');

mongoose.connect('mongodb://127.0.0.1:27017/main');
mongoose.set('debug', true);

mongoose.model('User', mongoose.Schema({ name: String }));

const app = express();

app.get('/users/:tenantId', function(req, res) {
  const db = mongoose.connection.useDb(`tenant_${req.params.tenantId}`, {
    // `useCache` tells Mongoose to cache connections by database name, so
    // `mongoose.connection.useDb('foo', { useCache: true })` returns the
    // same reference each time.
    useCache: true
  });
  // Need to register models every time a new connection is created
  if (!db.models['User']) {
    db.model('User', mongoose.Schema({ name: String }));
  }
  console.log('Find users from', db.name);
  db.model('User').find().
    then(users => res.json({ users })).
    catch(err => res.status(500).json({ message: err.message }));
});

app.listen(3000);

以下是结构(2)的示例。结构 (2) 更灵活,更适合超过 10k 租户且每秒超过 1 个请求的用例。由于每个租户都有独立的连接池,一个租户的缓慢操作对其他租户的影响很小。然而,这种结构在生产中更难实现和管理。特别是 MongoDB 确实对打开连接数有限制MongoDB Atlas 对打开连接数有单独的限制,因此你需要确保连接池中的套接字总数不超过 MongoDB 的限制。

¥The following is an example of pattern (2). Pattern (2) is more flexible and better for use cases with > 10k tenants and > 1 requests/second. Because each tenant has a separate connection pool, one tenants' slow operations will have minimal impact on other tenants. However, this pattern is harder to implement and manage in production. In particular, MongoDB does have a limit on the number of open connections, and MongoDB Atlas has separate limits on the number of open connections, so you need to make sure the total number of sockets in your connection pools doesn't go over MongoDB's limits.

const express = require('express');
const mongoose = require('mongoose');

const tenantIdToConnection = {};

const app = express();

app.get('/users/:tenantId', function(req, res) {
  let initialConnection = Promise.resolve();
  const { tenantId } = req.params;
  if (!tenantIdToConnection[tenantId]) {
    tenantIdToConnection[tenantId] = mongoose.createConnection(`mongodb://127.0.0.1:27017/tenant_${tenantId}`);
    tenantIdToConnection[tenantId].model('User', mongoose.Schema({ name: String }));
    initialConnection = tenantIdToConnection[tenantId].asPromise();
  }
  const db = tenantIdToConnection[tenantId];
  initialConnection.
    then(() => db.model('User').find()).
    then(users => res.json({ users })).
    catch(err => res.status(500).json({ message: err.message }));
});

app.listen(3000);

下一步

¥Next Up

现在我们已经介绍了连接,让我们看一下 models

¥Now that we've covered connections, let's take a look at models.