性能考虑
概述
在本指南中,您可以了解如何优化Rust驱动程序的性能。要连接到MongoDB,您必须创建一个Client
实例。您的Client
实例会自动处理连接的大部分方面,例如发现服务器拓扑、监控您的连接以及维护内部连接池。本指南描述了配置和使用您的Client
实例的最佳实践。
本指南包括以下部分
Client生命周期描述了创建和管理
Client
实例的最佳实践连接池描述了驱动程序中的连接池如何工作
并行处理提供了运行并行、异步任务的示例代码
运行时描述了如何通过使用
tokio
和async_std
crates的功能来管理运行时附加信息提供了本指南中提到的类型和方法的资源链接和API文档
Client生命周期
我们建议您在会话和操作之间重用您的客户端。您可以使用相同的Client
实例执行多个任务,因为Client
类型是线程安全的多线程并发使用的。为每个请求创建一个新的Client
实例会导致性能降低。
以下代码创建了一个接受现有Client
实例指针的方法,这使得您可以使用同一个客户端执行许多请求
// ... Create a client earlier in your code async fn make_request(client: &Client) -> Result<(), Box<dyn Error>> { // Use the client to perform operations Ok(()) }
连接池
每个 Client
实例都为 MongoDB 拓扑中的每个服务器内置了一个连接池。连接池在需要时打开套接字,以支持应用程序中对 MongoDB 的并发请求。
默认的 Client
配置适用于大多数应用程序。以下代码演示了如何创建具有默认连接设置的客户端
let client = Client::with_uri_str("<connection string>").await?;
或者,您可以调整连接池以最佳地满足应用程序的需求并优化性能。有关如何自定义连接设置的更多信息,请参阅本指南的以下子部分
提示
要了解更多有关配置连接池的信息,请参阅服务器手册中的调整您的连接池设置。
配置最大连接池大小
每个连接池的最大大小由 max_pool_size
选项设置,默认为 10
。如果到服务器的已用连接数达到 max_pool_size
的值,则该服务器下一次请求将等待直到连接可用。
除了支持应用程序请求所需的套接字外,每个 Client
实例还为您的 MongoDB 拓扑中的每个服务器打开两个额外的套接字来监控服务器状态。例如,连接到三个节点副本集的客户端打开六个监控套接字。如果应用程序使用 max_pool_size
的默认设置,并且仅查询主节点(默认),则连接池中最多可以有 16 个总连接。如果应用程序使用读取偏好查询辅助节点,则这些连接池会增长,总连接数可达 36。
要在一个进程内支持大量的并发 MongoDB 请求,您可以增加 max_pool_size
选项的值。以下代码演示了在实例化 Client
时如何指定 max_pool_size
的值。
let mut client_options = ClientOptions::parse("<connection string>").await?; client_options.max_pool_size = Some(20); let client = Client::with_options(client_options)?;
配置并发连接选项
连接池受速率限制。`max_connecting` 选项决定了连接池在任何时刻可以创建的并发连接数量。例如,如果 `max_connecting` 的值为 `2`,默认值,则尝试并发获取连接的第三次请求只有在以下情况之一发生时才会成功
连接池完成创建连接,并且连接池中的连接数小于或等于 `max_pool_size` 的值。
现有的连接被检查回连接池。
由于连接创建的速率限制,驱动程序重用现有连接的能力得到改善。
您可以使用 `min_pool_size` 选项设置每个服务器的最小并发连接数,该选项默认为 `0`。驱动程序使用此数字初始化连接池。如果套接字被关闭,并且使用中和空闲的套接字总数低于最小值,连接池将打开更多套接字,直到达到最小值。
以下代码在实例化 `Client` 时设置了 `max_connecting` 和 `min_pool_size` 选项
let mut client_options = ClientOptions::parse("<connection string>").await?; client_options.max_connecting = Some(3); client_options.min_pool_size = Some(1); let client = Client::with_options(client_options)?;
配置最大空闲时间
您可以通过设置 `max_idle_time` 选项来设置连接池中连接可以保持空闲的最大时间。一旦连接空闲时间达到 `max_idle_time` 指定的时间,连接池将删除并替换该连接。此选项默认为 `0`,或无限制。
当在您的应用程序中的任何位置调用Client::shutdown()
方法时,驱动程序将关闭所有空闲套接字,并在它们返回池中时关闭所有正在使用的套接字。调用Client::shutdown()
仅关闭非活动套接字,因此您不能使用此方法中断或终止任何正在进行的操作。驱动程序仅在进程完成时关闭这些套接字。
以下代码在实例化Client
时将max_idle_time
选项的值设置为90
秒。
let mut client_options = ClientOptions::parse("<connection string>").await?; client_options.max_idle_time = Some(Duration::new(90, 0)); let client = Client::with_options(client_options)?;
并行性
如果您可以运行并行数据操作,您可以通过运行异步、并发任务来优化性能。以下代码使用tokio::task
模块中的spawn()
方法创建单独的并发任务以执行插入操作。
let client = Client::with_uri_str("<connection string>").await?; let data = doc! { "title": "1984", "author": "George Orwell" }; for i in 0..5 { let client_ref = client.clone(); let data_ref = data.clone(); task::spawn(async move { let collection = client_ref .database("items") .collection::<Document>(&format!("coll{}", i)); collection.insert_one(data_ref).await }); }
运行时
一个Client
实例绑定到您在其中创建的tokio
或async-std
运行时实例。如果您使用Client
实例在另一个运行时上执行操作,您可能会遇到意外的行为或失败。
如果您使用tokio
或async_std
crate中的test
辅助宏来测试您的应用程序,您可能会意外地在非预期运行时上运行操作。这是因为这些辅助宏为每个测试创建一个新的运行时。但是,您可以使用以下策略之一来避免此问题。
不使用
test
辅助宏,将运行时附加到Client
实例。为每个
async
测试创建一个新的Client
实例。
本示例遵循第一种策略并创建了一个仅用于测试的全局运行时。在下面的代码中,test_list_dbs()
方法使用一个客户端手动连接到此运行时以列出部署中的数据库
use tokio::runtime::Runtime; use once_cell::sync::Lazy; static CLIENT_RUNTIME: Lazy<(Client, Runtime)> = Lazy::new(|| { let rt = Runtime::new().unwrap(); let client = rt.block_on(async { Client::with_uri_str("<connection string>").await.unwrap() }); (client, rt) }); fn test_list_dbs() -> Result<(), Box<dyn Error>> { let (client, rt) = &*CLIENT_RUNTIME; rt.block_on(async { client.list_database_names().await })?; Ok(()) }
实现第二种策略,以下代码为每个测试运行创建一个新的 Client
实例,使用 tokio::test
,确保运行时之间没有意外的交互
async fn test_list_dbs() -> Result<(), Box<dyn Error>> { let client = Client::with_uri_str("<connection string>").await?; client.list_database_names().await?; Ok(()) }
更多信息
要了解更多关于连接到 MongoDB 的信息,请参阅连接指南.
要了解关于 Rust 驱动程序可用运行时的更多信息,请参阅关于异步和同步 API 的指南。
API 文档
spawn()模块中的
tokio::task