最佳实践
使用以下最佳实践来正确托管 AWS Lambda 和 Atlas 之间的连接:
在 AWS Lambda 处理程序函数 外部定义 MongoDB 服务器的客户端。
请勿在每次调用函数时都定义新的客户端对象。这样做会导致驱动程序在每次函数调用时创建一个新的数据库连接。这可能会产生高昂的成本,并可能导致应用程序超出数据库连接限制。为获得最佳性能,请遵循以下准则:
创建一次客户端对象。
存储该对象,以便函数可以跨函数调用重复使用客户端。
连接示例重复使用现有数据库连接来加快与数据库的通信,并将数据库连接计数保持在相对于应用程序流量的合理水平。
- 如果您有一个连接到具有许多分片的分片集群的 Lambda 函数,可能会遇到性能问题。例如,对于具有 10 个分片的分片集群,默认情况下,驱动程序会连接到所有 30 个
mongos实例。您可以使用连接字符串中的srvMaxHosts选项来设置驱动程序连接到的主机的最大数量。要提高驱动程序性能,请设置srvMaxHosts=3。例如:mongodb+srv://<db_username>:<db_password>@<clusterName>.mongodb.net/?retryWrites=true&w=majority&srvMaxHosts=3
如果您有一个连接到具有许多分片的分片集群的 Lambda 函数,可能会遇到性能问题。例如,对于具有 10 个分片的分片集群,默认情况下,驱动程序会连接到所有 30 个
mongos实例。您可以使用连接字符串中的srvMaxHosts选项来设置驱动程序连接到的主机的最大数量。要提高驱动程序性能,请设置srvMaxHosts=3。例如:mongodb+srv://<db_username>:<db_password>@<clusterName>.mongodb.net/?retryWrites=true&w=majority&srvMaxHosts=3 要了解更多信息,请参阅《指定连接选项》指南。
如果您的处理程序将 回调 作为最后一个参数,请将 AWS Lambda Context 对象上的
callbackWaitsForEmptyEventLoop属性设置为 false。context.callbackWaitsForEmptyEventLoop = false; 这允许Lambda函数将其结果返回给调用者,而无需关闭MongoDB 数据库连接。设置此属性不适用于异步处理程序。
要将无服务器PHP应用程序部署到 AWS Lambda,请使用Bref框架。要安装必备组件并配置 Bref,请按照 Bref 文档中的设置教程进行操作。
接下来,您可以创建项目目录,安装MongoDB和 Bref 依赖项,并通过运行以下命令来初始化无服务器配置:
mkdir bref-mongodb-app && cd bref-mongodb-app composer init composer require bref/bref bref/extra-php-extensions mongodb/mongodb vendor/bin/bref init 最后,将MongoDB PHP扩展添加到您的项目中。
bref/extra-php-extensionsComposer包提供了一个无服务器框架插件,该插件将MongoDB PHP扩展添加为Lambda层。更新serverless.yml文件以包含以下突出显示的行:plugins: - ./vendor/bref/bref - ./vendor/bref/extra-php-extensions functions: api: handler: index.php runtime: php-85-fpm layers: - ${bref-extra:mongodb2-php-85}
如果您有一个连接到具有许多分片的分片集群的 Lambda 函数,可能会遇到性能问题。例如,对于具有 10 个分片的分片集群,默认情况下,驱动程序会连接到所有 30 个
mongos实例。您可以使用连接字符串中的srvMaxHosts选项来设置驱动程序连接到的主机的最大数量。要提高驱动程序性能,请设置srvMaxHosts=3。例如:mongodb+srv://<db_username>:<db_password>@<clusterName>.mongodb.net/?retryWrites=true&w=majority&srvMaxHosts=3 要了解更多信息,请参阅连接到 MongoDB 的工具。
如果您有一个连接到具有许多分片的分片集群的 Lambda 函数,可能会遇到性能问题。例如,对于具有 10 个分片的分片集群,默认情况下,驱动程序会连接到所有 30 个
mongos实例。您可以使用连接字符串中的srvMaxHosts选项来设置驱动程序连接到的主机的最大数量。要提高驱动程序性能,请设置srvMaxHosts=3。例如:mongodb+srv://<db_username>:<db_password>@<clusterName>.mongodb.net/?retryWrites=true&w=majority&srvMaxHosts=3 要了解更多信息,请参阅 URI 选项。
- 如果您有一个连接到具有许多分片的分片集群的 Lambda 函数,可能会遇到性能问题。例如,对于具有 10 个分片的分片集群,默认情况下,驱动程序会连接到所有 30 个
mongos实例。您可以使用连接字符串中的srvMaxHosts选项来设置驱动程序连接到的主机的最大数量。要提高驱动程序性能,请设置srvMaxHosts=3。例如:mongodb+srv://<db_username>:<db_password>@<clusterName>.mongodb.net/?retryWrites=true&w=majority&srvMaxHosts=3
注意
如果您将任何 IPv4 /0 CIDR(例如 0.0.0.0/0 或 10.0.0.0/0)添加到项目访问权限列表, Atlas会向所有直接授予项目角色或间接授予项目角色的用户发送一封警报电子邮件团队成员身份(如果团队被授予项目角色)。
设置统一 AWS 访问权限,并尽可能使用 AWS IAM 身份验证。
您可以使用Amazon Web Services IAM 角色连接到Atlas集群,而不是在Lambda中硬编码您的凭证。任何访问您的Amazon Web Services Lambda环境的人都可以查看硬编码凭证,这可能会带来安全风险。 借助 Amazon Web Services IAM身份验证, Atlas可通过 假定的 IAM角色 访问 Amazon Web Services Lambda ,因此您无需在连接字符串中提供凭证。
Atlas 支持对运行 MongoDB 7.0 或更高版本的集群进行 AWS IAM 身份验证。如果您的集群符合这些要求,则强烈建议将 AWS IAM 身份验证用于 Lambda 连接。
分配给 Lambda 函数的内存量默认为 128 MB。您可将分配给 Lambda 函数的内存量配置为 128 MB 到 10,240 MB 之间。请务必分配足够的内存。增大内存以增加可用虚拟 CPU 的内存量并提高 MongoDB 驱动程序性能。要了解详情,请参阅内存和计算能力。
设置 AWS_STS_REGIONAL_ENDPOINTS 和 AWS_REGION 环境变量。
连接示例
void lambda_handler () { bson_error_t error; mongoc_init(); // Parse URI char *uri_string = "your-mongodb-atlas-connection-string"; mongoc_uri_t *uri = mongoc_uri_new_with_error (uri_string, &error); if (!uri) { fprintf (stderr, "Failed to parse URI: %s\n", error.message); return; } // Create client mongoc_client_t *client = mongoc_client_new_from_uri_with_error(uri, &error); if (!client) { fprintf(stderr, "Failed to create client: %s\n", error.message); return; } // Perform client operations here // Cleanup mongoc_client_destroy(client); mongoc_uri_destroy (uri); mongoc_cleanup(); }
using namespace aws::lambda_runtime; class ExampleAwsHandler { private: mongocxx::client mongo_client; client CreateMongoClient() { mongocxx::uri uri("mongodb://<hostname>:<port>/?authMechanism=MONGODB-AWS"); mongocxx::options::server_api server_api_options(mongocxx::options::server_api::version::k_version_1); mongocxx::options::client client_options; client_options.server_api_opts(server_api_options); return client(uri, client_options); } public: ExampleAwsHandler() : mongo_client(CreateMongoClient()) { } std::string HandleRequest() { try { using bsoncxx::builder::basic::kvp; using bsoncxx::builder::basic::make_document; auto db = mongo_client["my_database"]; auto command = make_document(kvp("hello", 1)); auto result = db.run_command(command.view()); return bsoncxx::to_json(result); } catch (const mongocxx::exception &e) { std::cerr << "MongoDB Exception: " << e.what() << std::endl; return "{}"; } } }; static invocation_response my_handler(invocation_request const &) { ExampleAwsHandler handler; std::string response = handler.HandleRequest(); return invocation_response::success(response, "application/json"); } int main() { Aws::SDKOptions options; Aws::InitAPI(options); { mongocxx::instance instance{}; run_handler(my_handler); } Aws::ShutdownAPI(options); return 0; }
AWS IAM 身份验证
string username = Environment.GetEnvironmentVariable("AWS_ACCESS_KEY_ID"); string password = Environment.GetEnvironmentVariable("AWS_SECRET_ACCESS_KEY"); string awsSessionToken = Environment.GetEnvironmentVariable("AWS_SESSION_TOKEN"); var awsCredentials = new MongoCredential("MONGODB-AWS", new MongoExternalIdentity(username), new PasswordEvidence(password)) .WithMechanismProperty("AWS_SESSION_TOKEN", awsSessionToken); var mongoUrl = MongoUrl.Create($"<MONGODB_URI>"); var mongoClientSettings = MongoClientSettings.FromUrl(mongoUrl); mongoClientSettings.Credential = awsCredentials; mongoClientSettings.ServerApi = new ServerApi(ServerApiVersion.V1, strict: true); return new MongoClient(mongoClientSettings);
其他身份验证
private static MongoClient MongoClient { get; set; } private static MongoClient CreateMongoClient() { var mongoClientSettings = MongoClientSettings.FromConnectionString($"<MONGODB_URI>"); mongoClientSettings.ServerApi = new ServerApi(ServerApiVersion.V1, strict: true); return new MongoClient(mongoClientSettings); } static ShareMongoClientLambdaHandler() { MongoClient = CreateMongoClient(); } public string HandleRequest(ILambdaContext context) { var database = MongoClient.GetDatabase("db"); var collection = database.GetCollection<BsonDocument>("coll"); var result = collection.Find(FilterDefinition<BsonDocument>.Empty).First(); return result.ToString(); }
import ( "context" "os" runtime "github.com/aws/aws-lambda-go/lambda" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" ) var client, err = mongo.Connect(options.Client().ApplyURI(os.Getenv("MONGODB_URI"))) func HandleRequest(ctx context.Context) error { if err != nil { return err } return client.Ping(context.TODO(), nil) }
import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import org.bson.Document; public class ExampleAwsLambdaHandler implements RequestHandler<String, String> { private final MongoClient client; public ExampleAwsLambdaHandler() { client = MongoClients.create(System.getenv("MONGODB_URI")); } public String handleRequest(final String input, final Context context) { return client.getDatabase("admin").runCommand(new Document("ping", 1)).toJson(); } }
import com.mongodb.kotlin.client.coroutine.MongoClient import com.mongodb.kotlin.client.coroutine.MongoDatabase import org.bson.Document import kotlinx.coroutines.runBlocking import com.amazonaws.services.lambda.runtime.Context import com.amazonaws.services.lambda.runtime.RequestHandler class ExampleAwsLambdaHandler : RequestHandler<String, String> { private val client: MongoClient = MongoClient.create(System.getenv("MONGODB_URI")) override fun handleRequest(input: String, context: Context): String = runBlocking { val database: MongoDatabase = client.getDatabase("admin") val command = Document("ping", 1) val result = database.runCommand(command) result.toJson() } }
import com.mongodb.kotlin.client.MongoClient import com.mongodb.kotlin.client.MongoDatabase import org.bson.Document import com.amazonaws.services.lambda.runtime.Context import com.amazonaws.services.lambda.runtime.RequestHandler class ExampleAwsLambdaHandler : RequestHandler<String, String> { private val client: MongoClient = MongoClient.create(System.getenv("MONGODB_URI")) override fun handleRequest(input: String, context: Context): String { val database: MongoDatabase = client.getDatabase("admin") val command = Document("ping", 1) val result = database.runCommand(command) return result.toJson() } }
AWS IAM 身份验证
const { MongoClient } = require('mongodb'); // Get the URI for the cluster then set AWS_ACCESS_KEY_ID as the username in the // URI and AWS_SECRET_ACCESS_KEY as the password, then set the appropriate auth // options. Note that MongoClient now auto-connects so no need to store the connect() // promise anywhere and reference it. const client = new MongoClient(process.env.MONGODB_URI, { auth: { username: process.env.AWS_ACCESS_KEY_ID, password: process.env.AWS_SECRET_ACCESS_KEY }, authSource: '$external', authMechanism: 'MONGODB-AWS' }); module.exports.handler = async function () { const databases = await client.db('admin').command({ listDatabases: 1 }); return { statusCode: 200, databases: databases }; };
其他身份验证
const { MongoClient } = require('mongodb'); // MongoClient now auto-connects so no need to store the connect() // promise anywhere and reference it. const client = new MongoClient(process.env.MONGODB_URI); module.exports.handler = async function () { const databases = await client.db('admin').command({ listDatabases: 1 }); return { statusCode: 200, databases: databases }; };
设置Amazon Web Services档案
配置 AWS IAM身份验证后,从Atlas用户界面复制连接字符串。从连接字符串中删除 <AWS access key>:<AWS secret key> 部分,因为驱动程序会从环境变量中读取这些凭证。在项目的 serverless.yml文件中,将 MONGODB_URI 环境变量设立为此修改后的连接字符串,如以下代码所示:
provider: environment: MONGODB_URI: "<connection string without credentials>"
定义Lambda函数
将 index.php文件的内容替换为以下代码:
require 'vendor/autoload.php'; use MongoDB\Client; // Create the MongoDB client outside the handler function $client = new Client(getenv('MONGODB_URI')); function handler($event, $context) { global $client; $database = $client->getDatabase('admin'); $result = $database->command(['ping' => 1]); return [ 'statusCode' => 200, 'body' => json_encode($result) ]; }
部署应用程序
要部署应用程序,运行以下命令:
serverless deploy
import os from pymongo import MongoClient client = MongoClient(host=os.environ["MONGODB_URI"]) def lambda_handler(event, context): return client.db.command("ping")
AWS IAM 身份验证
# Require the driver library. require "mongo" # Create a Mongo::Client instance using AWS IAM authentication. # CRITICAL: You must create the client instance outside the handler # so that the client can be reused across function invocations. client = Mongo::Client.new([ENV.fetch("MONGODB_HOST")], auth_mech: :aws, user: ENV.fetch("AWS_ACCESS_KEY_ID"), password: ENV.fetch("AWS_SECRET_ACCESS_KEY"), auth_mech_properties: { aws_session_token: ENV.fetch("AWS_SESSION_TOKEN"), }, database: ENV.fetch("MONGODB_DATABASE")) def lambda_handler(event:, context:) # Use the client to return the name of the configured database. client.database.name end
其他身份验证
# Require the driver library. require "mongo" # Create a Mongo::Client instance. # CRITICAL: You must create the client instance outside the handler # so that the client can be reused across function invocations. client = Mongo::Client.new(ENV.fetch("MONGODB_URI")) def lambda_handler(event:, context:) # Use the client to return the name of the configured database. client.database.name end
AWS IAM 身份验证
use lambda_runtime::{service_fn, LambdaEvent}; use mongodb::{ bson::doc, options::{AuthMechanism, ClientOptions, Credential}, Client, }; use serde_json::Value; use tokio::sync::OnceCell; // Initialize a global static MongoDB Client with AWS authentication. The following environment // variables should also be set: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and, optionally, // AWS_SESSION_TOKEN. static MONGODB_CLIENT: OnceCell<Client> = OnceCell::const_new(); async fn get_mongodb_client() -> &'static Client { MONGODB_CLIENT .get_or_init(|| async { let uri = std::env::var("MONGODB_URI") .expect("MONGODB_URI must be set to the URI of the MongoDB deployment"); let mut options = ClientOptions::parse(&uri) .await .expect("Failed to parse options from URI"); let credential = Credential::builder() .mechanism(AuthMechanism::MongoDbAws) .build(); options.credential = Some(credential); Client::with_options(options).expect("Failed to create MongoDB Client") }) .await } // Runs a ping operation on the "db" database and returns the response. async fn handler(_: LambdaEvent<Value>) -> Result<Value, lambda_runtime::Error> { let client = get_mongodb_client().await; let response = client .database("db") .run_command(doc! { "ping": 1 }) .await?; let json = serde_json::to_value(response)?; Ok(json) } async fn main() -> Result<(), lambda_runtime::Error> { let service = service_fn(handler); lambda_runtime::run(service).await?; Ok(()) }
其他身份验证
use lambda_runtime::{service_fn, LambdaEvent}; use mongodb::{bson::doc, Client}; use serde_json::Value; use tokio::sync::OnceCell; // Initialize a global static MongoDB Client. static MONGODB_CLIENT: OnceCell<Client> = OnceCell::const_new(); async fn get_mongodb_client() -> &'static Client { MONGODB_CLIENT .get_or_init(|| async { let uri = std::env::var("MONGODB_URI") .expect("MONGODB_URI must be set to the URI of the MongoDB deployment"); Client::with_uri_str(uri) .await .expect("Failed to create MongoDB Client") }) .await } // Runs a ping operation on the "db" database and returns the response. async fn handler(_: LambdaEvent<Value>) -> Result<Value, lambda_runtime::Error> { let client = get_mongodb_client().await; let response = client .database("db") .run_command(doc! { "ping": 1 }) .await?; let json = serde_json::to_value(response)?; Ok(json) } async fn main() -> Result<(), lambda_runtime::Error> { let service = service_fn(handler); lambda_runtime::run(service).await?; Ok(()) }
import org.mongodb.scala.{MongoClient, Document} import scala.concurrent.Await import scala.concurrent.duration.Duration import com.amazonaws.services.lambda.runtime.Context import com.amazonaws.services.lambda.runtime.RequestHandler class ExampleAwsLambdaHandler extends RequestHandler[String, String] { private val client: MongoClient = MongoClient(System.getenv("MONGODB_URI")) override def handleRequest(input: String, context: Context): String = { val database = client.getDatabase("admin") val command = Document("ping" -> 1) val futureResult = database.runCommand(command).toFuture() val result = Await.result(futureResult, Duration(30, "second")) result.toJson() } }