Make the MongoDB docs better! We value your opinion. Share your feedback for a chance to win $100.
Click here >
Docs Menu
Docs Home
/ /

Construya una aplicación resiliente con MongoDB

Para escribir código de aplicación que aproveche al máximo las capacidades de MongoDB y gestione con elegancia las elecciones de set de réplicas, debes:

  • Instala las últimas Librerías de clientes.

  • Utilice una cadena de conexión que especifique todos los hosts.

  • Utilice escrituras reintentables y lecturas reintentables.

  • Utiliza un majority escriba el nivel de confirmación de escritura (write concern) y el nivel de consistencia de lectura que tenga sentido para su aplicación.

  • Gestiona los errores en tu aplicación.

Primero, instala la biblioteca cliente más reciente para tu lenguaje desde Librerías de clientes de MongoDB. Las Librerías de clientes se conectan y transmiten queries desde tu aplicación a tu base de datos. Utilizar las últimas Librerías de clientes permite acceder a las últimas funcionalidades de MongoDB.

Luego, en tu aplicación, importa la dependencia:

Si estás utilizando Maven, agregue lo siguiente en su lista de dependencias de pom.xml:

<dependencies>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>

Si está utilizando Gradle, añada lo siguiente a su lista de dependencias build.gradle:

dependencies {
compile 'org.mongodb:mongodb-driver-sync:4.0.1'
}
// Latest 'mongodb' version installed with npm
const MongoClient = require('mongodb').MongoClient;

Utiliza un cadena de conexión que especifique todos los hosts en tu implementación para conectar tu aplicación a tu base de datos. Si la implementación realiza una elección de set de réplicas y se elige un nuevo primario, una cadena de conexión que especifique todos los hosts de la implementación descubre el nuevo primario sin lógica de aplicación.

Puedes especificar todos los hosts en tu implementación utilizando:

La cadena de conexión también puede especificar opciones, especialmente retryWrites y writeConcern.

Tip

Para obtener ayuda sobre cómo dar formato a tu cadena de conexión, consulta Conectar a una Implementación utilizando una biblioteca cliente MongoDB.

Utiliza tu cadena de conexión para crear un cliente de MongoDB en la aplicación:

// Create a variable for your connection string
String uri = "mongodb://[<username>:<password>@]hostname0<:port>[,hostname1:<port1>][,hostname2:<port2>][...][,hostnameN:<portN>]";
// Instantiate the MongoDB client with the URI
MongoClient client = MongoClients.create(uri);
// Create a variable for your connection string
const uri = "mongodb://[<username>:<password>@]hostname0<:port>[,hostname1:<port1>][,hostname2:<port2>][...][,hostnameN:<portN>]";
// Instantiate the MongoDB client with the URI
const client = new MongoClient(uri);

Nota

A partir de la versión 3.6 de MongoDB y con Librerías de clientes compatibles con4.2, MongoDB vuelve a intentar tanto las escrituras como las lecturas una vez por defecto.

Utilice escrituras retratables para reintentar ciertas operaciones de escritura una sola vez si fallan.

Reintentar las escrituras exactamente una vez es la mejor estrategia para manejar errores transitorios de red y elección del set de réplicas en las que la aplicación no puede encontrar temporalmente un nodo primario en buen estado. Si el reintento tiene éxito, la operación en su conjunto es exitosa y no se devuelve ningún error. Si la operación falla, es probable que sea debido a:

  • Un error de red persistente, o

  • Un comando no válido.

Tip

Para más información sobre cómo habilitar escrituras reprocesables, consulte Cómo habilitar escrituras reprocesables.

Cuando una operación falla, su aplicación necesita gestionar el error ella misma.

Las operaciones de lectura se reintentan automáticamente una sola vez si fallan a partir de la versión 3.6 de MongoDB y con Librerías de clientes compatibles con4.2. No es necesario configurar la aplicación para volver a intentar las lecturas.

Puedes ajustar la coherencia y la disponibilidad de tu aplicación utilizando niveles de confirmación de escritura (write concern) y niveles de consistencia de lectura. Preocupaciones más estrictas implican que las operaciones de la base de datos esperen garantías de coherencia de datos más fuertes, mientras que el alivio de los requisitos de coherencia proporciona una mayor disponibilidad.

Ejemplo

Si su aplicación gestiona saldos monetarios, la coherencia es sumamente importante. Puedes usar majority guardar y nivel de consistencia de lectura para asegurarte de nunca leer datos obsoletos o datos que puedan ser revertidos.

Alternativamente, si tu aplicación registra datos de temperatura de cientos de sensores cada segundo, es posible que no te preocupe si lees datos que no incluyen las lecturas más recientes. Puede flexibilizar los requisitos de coherencia para proporcionar un acceso más rápido a esos datos.

Puede establecer el nivel de confirmación de escritura (write concern) de su conjunto de réplicas a través del URI de cadena de conexión. Utiliza un majority nivel de confirmación de escritura (write concern) para asegurarte de que tus datos se guarden correctamente en tu base de datos y se conserven. Esta es la configuración recomendada por defecto, suficiente para la mayoría de los casos de uso.

Cuando use un nivel de confirmación de escritura (write concern) que requiera confirmación, como majority, también puede especificar un límite máximo de tiempo para que las escrituras alcancen ese nivel de confirmación:

  • El parámetro de la cadena de conexión wtimeoutMS para todas las escrituras, o

  • La opción wtimeout para una única operación de guardar.

El uso o no de un límite de tiempo y el valor que utilices dependen del contexto de tu aplicación.

Tip

Para obtener más información sobre cómo establecer los niveles de nivel de confirmación de escritura (write concern), consulta Opciones de nivel de confirmación de escritura (write concern).

Importante

Si no se especifica un límite de tiempo para las escrituras y el nivel de confirmación de escritura es inalcanzable, la operación de escritura nunca se completará.

Puedes configurar el nivel de consistencia de lectura de tu set de réplicas a través del URI de cadena de conexión. El nivel de consistencia de lectura ideal depende de los requisitos de tu aplicación, pero por defecto es suficiente para la mayoría de los casos de uso. No se requiere ningún parámetro de cadena de conexión para usar los niveles de consistencia de lectura por defecto.

Especificar un nivel de consistencia de lectura puede mejorar las garantías sobre los datos que tu aplicación recibe de la base de datos.

Tip

Para obtener más información sobre cómo configurar los niveles de consistencia de lectura, consulte Opciones de consistencia de lectura.

Nota

La combinación específica de guardar y nivel de consistencia de lectura que utiliza su aplicación afecta las garantías del orden de las operaciones. Esto se denomina coherencia causal. Para obtener más información sobre las garantías de la coherencia causal, consulta Coherencia causal y nivel de confirmación de escritura (write concern).

Los comandos no válidos, las interrupciones del servicio y los errores de red que no son gestionados por escrituras reintentables devuelven errores. Consulte la librería de cliente API documentación para detalles del error.

Por ejemplo, si una aplicación intenta insertar un documento con un _id duplicado, la biblioteca cliente retorna un error que incluye:

Unable to insert due to an error: com.mongodb.MongoWriteException:
E11000 duplicate key error collection: <db>.<collection> ...
{
"name": : "MongoError",
"message": "E11000 duplicate key error collection on: <db>.<collection> ... ",
...
}

Sin un manejo adecuado de errores, un error podría impedir que tu aplicación realice el procesamiento de solicitudes hasta que se reinicie.

Tu aplicación debe gestionar errores sin fallos ni efectos secundarios. En el ejemplo anterior de una aplicación que inserta un duplicado _id, esa aplicación podría gestionar los errores de la siguiente manera:

// Declare a logger instance from java.util.logging.Logger
private static final Logger LOGGER = ...
...
try {
InsertOneResult result = collection.insertOne(new Document()
.append("_id", 1)
.append("body", "I'm a goofball trying to insert a duplicate _id"));
// Everything is OK
LOGGER.info("Inserted document id: " + result.getInsertedId());
// Refer to the API documentation for specific exceptions to catch
} catch (MongoException me) {
// Report the error
LOGGER.severe("Failed due to an error: " + me);
}
...
collection.insertOne({
_id: 1,
body: "I'm a goofball trying to insert a duplicate _id"
})
.then(result => {
response.sendStatus(200) // send "OK" message to the client
},
err => {
response.sendStatus(400); // send "Bad Request" message to the client
});

La operación de inserción en este ejemplo arroja un error de "clave duplicada" la segunda vez que se invoca porque el campo _id debe ser único. La aplicación captura el error, el cliente es notificado y la aplicación continúa ejecutándose. Sin embargo, la operación de inserción falla y queda a criterio del usuario decidir si mostrar un mensaje, reintentar la operación o hacer otra cosa.

Siempre se deben registrar los errores. Las estrategias comunes para el procesamiento adicional de errores incluyen:

  • Devuelva el error al cliente junto con un mensaje de error. Esta es una buena estrategia cuando no se puede resolver el error y es necesario informar al usuario que no se puede completar una acción.

  • Escribe en una base de datos de copias de seguridad. Esta es una buena estrategia cuando no puedes resolver el error pero no quieres arriesgarte a perder los datos de la solicitud.

  • Vuelva a intentar la operación más allá del único reintento por defecto. Esta es una buena estrategia cuando puedes resolver la causa de un error programáticamente y luego volver a intentarlo.

Debe seleccionar las mejores estrategias para el contexto de su aplicación.

Ejemplo

En el ejemplo de un error de clave duplicada, debe registrar el error pero no volver a intentar la operación porque nunca tendrá éxito. En su lugar, puedes guardar en una base de datos alternativa y revisar el contenido de esa base de datos en un momento posterior para garantizar que no se pierda ninguna información. El usuario no necesita hacer nada más y los datos están registrados, por lo que puedes optar por no enviar un mensaje de error al cliente.

Devolver un error puede ser un comportamiento deseable cuando una operación nunca se completaría y bloquearía tu aplicación para ejecutar nuevas operaciones. Puedes usar el método maxTimeMS para establecer un límite de tiempo en operaciones individuales, devolviendo un error para que tu aplicación lo gestione si se excede ese límite de tiempo.

El límite de tiempo que se establezca para cada operación depende del contexto de esa operación.

Ejemplo

Si tu aplicación lee y muestra información de producto sencilla desde una colección inventory, puedes tener la confianza razonable de que esas operaciones de lectura solo toman un momento. Una query que se ejecuta inusualmente durante mucho tiempo es un buen indicador de que hay un problema de red persistente. Configurar maxTimeMS en esa operación a 5000, o 5 segundos, significa que tu aplicación recibe retroalimentación tan pronto como estés seguro de que hay un problema de red.

La siguiente aplicación de ejemplo reúne las recomendaciones para crear aplicaciones resilientes.

La aplicación es un sencillo registro de usuario API que expone dos puntos finales en http://localhost:3000:

Método
Endpoint
Descripción

GET

/users

Obtiene una lista de nombres de usuario de una colección users.

POST

/users

Requiere un name en el cuerpo de la solicitud. Agrega un nuevo usuario a una colección users.

Nota

La siguiente aplicación de servidor utiliza NanoHTTPD y json que debes añadir a tu proyecto como dependencias antes de poder ejecutarla.

1// File: App.java
2
3import java.util.Map;
4import java.util.logging.Logger;
5
6import org.bson.Document;
7import org.json.JSONArray;
8
9import com.mongodb.MongoException;
10import com.mongodb.client.MongoClient;
11import com.mongodb.client.MongoClients;
12import com.mongodb.client.MongoCollection;
13import com.mongodb.client.MongoDatabase;
14
15import fi.iki.elonen.NanoHTTPD;
16
17public class App extends NanoHTTPD {
18 private static final Logger LOGGER = Logger.getLogger(App.class.getName());
19
20 static int port = 3000;
21 static MongoClient client = null;
22
23 public App() throws Exception {
24 super(port);
25
26 // Replace the uri string with your MongoDB deployment's connection string
27 String uri = "mongodb://<username>:<password>@hostname0:27017,hostname1:27017,hostname2:27017/?retryWrites=true&w=majority";
28 client = MongoClients.create(uri);
29
30 start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
31 LOGGER.info("\nStarted the server: http://localhost:" + port + "/ \n");
32 }
33
34 public static void main(String[] args) {
35 try {
36 new App();
37 } catch (Exception e) {
38 LOGGER.severe("Couldn't start server:\n" + e);
39 }
40 }
41
42 @Override
43 public Response serve(IHTTPSession session) {
44 StringBuilder msg = new StringBuilder();
45 Map<String, String> params = session.getParms();
46
47 Method reqMethod = session.getMethod();
48 String uri = session.getUri();
49
50 if (Method.GET == reqMethod) {
51 if (uri.equals("/")) {
52 msg.append("Welcome to my API!");
53 } else if (uri.equals("/users")) {
54 msg.append(listUsers(client));
55 } else {
56 msg.append("Unrecognized URI: ").append(uri);
57 }
58 } else if (Method.POST == reqMethod) {
59 try {
60 String name = params.get("name");
61 if (name == null) {
62 throw new Exception("Unable to process POST request: 'name' parameter required");
63 } else {
64 insertUser(client, name);
65 msg.append("User successfully added!");
66 }
67 } catch (Exception e) {
68 msg.append(e);
69 }
70 }
71
72 return newFixedLengthResponse(msg.toString());
73 }
74
75 static String listUsers(MongoClient client) {
76 MongoDatabase database = client.getDatabase("test");
77 MongoCollection<Document> collection = database.getCollection("users");
78
79 final JSONArray jsonResults = new JSONArray();
80 collection.find().forEach((result) -> jsonResults.put(result.toJson()));
81
82 return jsonResults.toString();
83 }
84
85 static String insertUser(MongoClient client, String name) throws MongoException {
86 MongoDatabase database = client.getDatabase("test");
87 MongoCollection<Document> collection = database.getCollection("users");
88
89 collection.insertOne(new Document().append("name", name));
90 return "Successfully inserted user: " + name;
91 }
92}

Nota

La siguiente aplicación de servidor utiliza Express, que debes agregar como una dependencia a tu proyecto antes de poder ejecutarlo.

1const express = require('express');
2const bodyParser = require('body-parser');
3
4// Use the latest client libraries by installing & importing them
5const MongoClient = require('mongodb').MongoClient;
6
7const app = express();
8app.use(bodyParser.json());
9app.use(bodyParser.urlencoded({ extended: true }));
10
11// Use a connection string that lists all hosts
12// with retryable writes & majority write concern
13const uri = "mongodb://<username>:<password>@hostname0:27017,hostname1:27017,hostname2:27017/?retryWrites=true&w=majority";
14
15const client = new MongoClient(uri);
16
17// ----- API routes ----- //
18app.get('/', (req, res) => res.send('Welcome to my API!'));
19
20app.get('/users', (req, res) => {
21 const collection = client.db("test").collection("users");
22
23 collection
24 .find({})
25 // In this example, 'maxTimeMS' throws an error after 5 seconds,
26 // alerting the application to a lasting network outage
27 .maxTimeMS(5000)
28 .toArray((err, data) => {
29 if (err) {
30 // Handle errors in your application
31 // In this example, by sending the client a message
32 res.send("The request has timed out. Please check your connection and try again.");
33 }
34 return res.json(data);
35 });
36});
37
38app.post('/users', (req, res) => {
39 const collection = client.db("test").collection("users");
40 collection.insertOne({ name: req.body.name })
41 .then(result => {
42 res.send("User successfully added!");
43 }, err => {
44 // Handle errors in your application
45 // In this example, by sending the client a message
46 res.send("An application error has occurred. Please try again.");
47 })
48});
49// ----- End of API routes ----- //
50
51app.listen(3000, () => {
52 console.log(`Listening on port 3000.`);
53 client.connect(err => {
54 if (err) {
55 console.log("Not connected: ", err);
56 process.exit(0);
57 }
58 console.log('Connected.');
59 });
60});

Volver

Telemetría

En esta página