最佳实践
使用以下最佳实践来正确管理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
如果您有一个连接到具有许多分片的分片集群的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
如果您有一个连接到具有许多分片的分片集群的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
如果您有一个连接到具有许多分片的分片集群的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函数,则可能会遇到性能问题。示例,对于有 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 要将无服务器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
如果您有一个连接到具有许多分片的分片集群的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
限制对 Atlas 集群的网络访问。
使用Atlas 集群和 AWS Lambda函数之间的网络对等互连连接,或者私有端点,通过私有网络连接到Atlas 集群,这样您就可以仅允许IP访问列表中的私有IP地址。
如果您不使用私有网络,请考虑通过具有映射弹性IP解决 的NAT 网关连接到Atlas 集群。否则,您必须允许所有IP地址 (0.0.0.0/0)访问权限您的服务集群。
注意
如果您将任何 IPv4 /0 CIDR(例如 0.0.0.0/0 或 10.0.0.0/0)添加到项目访问列表中,Atlas 会向所有直接被授予该项目角色的用户发送告警邮件;对于通过团队成员身份间接获得项目角色的用户(前提是该团队已被授予项目角色),Atlas 也会发送告警邮件。
设置 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() } }