paint-brush
Cierre elegante en NodeJSpor@nairi_harutyunyan
93,861 lecturas
93,861 lecturas

Cierre elegante en NodeJS

por Nairi Harutyunyan7m2020/02/04
Read on Terminal Reader
Read this story w/o Javascript

Demasiado Largo; Para Leer

En este artículo, le mostraré cómo hacer un apagado correcto en la aplicación NodeJS. Necesitamos manejar todas las solicitudes y cerrar todos los recursos que se procesan en los datos (es decir, las conexiones de la base de datos) y no aceptar ninguna otra solicitud y, después de eso, puede apagar su servidor con la conciencia tranquila. El apagado correcto significa que todas sus solicitudes al servidor responden y no queda ningún trabajo de procesamiento de datos. No puede saber qué puede pasar con las solicitudes que se han realizado a su servidor si apaga el servidor inmediatamente.
featured image - Cierre elegante en NodeJS
Nairi Harutyunyan HackerNoon profile picture

En este artículo, le mostraré cómo hacer un apagado ordenado en la aplicación NodeJS, pero primero, describamos qué significa "cierre ordenado" y por qué necesitamos hacerlo en nuestra aplicación y cuáles son los beneficios.

Apagado elegante

Imaginemos que tiene un servidor HTTP con NodeJS que se conecta a una base de datos, y cada vez que se llama al servidor, envía una solicitud a la base de datos para obtener/establecer datos que también se enviarán al cliente por la respuesta.
Imagine que necesita apagar el servidor, la forma más fácil de hacerlo es <Ctrl>+C y el servidor se cerrará, pero espere, ¿qué pasa si su servidor no finalizó todas las solicitudes, qué pasa si algunas conexiones de clientes están cerradas porque el servidor se mata y no puede manejar las solicitudes.

-Eso te da un punto para pensar, ¿no?

Como puede suponer, debe manejar todas las solicitudes y cerrar todos los recursos que se procesan en los datos (es decir, las conexiones de la base de datos) y no aceptar ninguna otra solicitud y, después de eso, puede apagar su servidor con la conciencia tranquila.

El apagado correcto significa que todas sus solicitudes al servidor responden y no queda ningún trabajo de procesamiento de datos.

Es muy importante crear un apagado correcto y apagar su servidor correctamente porque no puede saber qué puede pasar con las solicitudes que se han realizado al servidor si apaga el servidor inmediatamente, puede cometer un error y matar el otro proceso ( byPID) que proporcionó al servidor NodeJS o pueden suceder algunas otras cosas, lo que podría ser malo para su aplicación.

como funciona eso

Estos son los cuatro pasos de cómo puede hacer un apagado correcto de una manera fácil.

  • Manejar la señal de eliminación del proceso
  • Detener nuevas solicitudes del cliente
  • Cerrar todo el proceso de datos
  • Salir del proceso

En primer lugar, debemos manejar el servidor que alguien quiere cerrar, después de eso, debemos completar todas las solicitudes y dejar de recibir nuevas solicitudes de los usuarios en el servidor, para que podamos estar seguros de que no hay solicitudes pendientes de usuarios al servidor antes de apagar el servidor, después de eso, debemos cerrar todo el procesamiento de datos (es decir, bases de datos, algunos trabajadores del sistema de archivos, etc.), depende de lo que esté haciendo en el servidor, y finalmente podemos salir del proceso

Vamos a crear un servidor NodeJS simple y hacer todos los pasos que mencioné anteriormente que harán un apagado correcto cuando queramos cerrar el servidor y entender cómo funciona.

Aquí hay un ejemplo simple de servidor NodeJS usando ExpressJS

 const express = require ( 'express' ); const mongoose = require ( 'mongoose' ); const app = express(); app.use(express.urlencoded({extended: true })); app.use(express.json()); mongoose.connect( 'mongodb://localhost/test' , ( err ) => { if (err) throw err; console .log( 'Mongoose connected!' ); }); const User = mongoose.model( 'User' , { name: String }); app.post( '/user' , async (req, res) => { try { const user = new User({ name: req.body.username }); await user.save(); res.send( 'Success!' ).status( 201 ); } catch (err) { res.send(err.message).status( 500 ); } }); app.listen( 3000 , () => console .log( 'Example app listening on port 3000!' ));

Aquí tenemos un servidor simple que tiene una ruta que crea un usuario en MongoDB.

Podemos probar el servidor y crear un usuario en la base de datos usando este comando

 curl -d '{ “username”: “Narek” }' -H “Content-Type: application/json” -X POST http: //localhost:3000/user

si tienes

 Success!
mensaje, entonces puede buscar los datos JSON en MongoDB.

Ahora repasemos los cuatro pasos y escribamos el código apropiado para eso.

1. Manejar la señal de eliminación del proceso

Primero, comprendamos qué es una señal de proceso.

Una señal es una notificación asincrónica enviada a un proceso oa un subproceso específico para notificar un evento que ocurrió.

Los eventos de señal se emitirán cuando el proceso de NodeJS reciba una señal.
Cada señal tiene un nombre (es decir, 'SIGINT', 'SIGTERM', etc.), más sobre eso en NodeJS aquí .

'SIGINT' generado con <Ctrl>+C en la terminal. La señal 'SIGTERM' es una señal genérica que se usa para provocar la finalización del programa. A diferencia de 'SIGKILL', esta señal se puede bloquear, manejar e ignorar. Es la forma normal de pedir cortésmente que un programa termine. El comando de shell kill genera 'SIGTERM' por defecto.

Puede leer más sobre las señales de terminación aquí .

Como puede suponer, necesitamos agregar un controlador que recibirá la señal 'SIGTERM'.

Aquí está el siguiente ejemplo basado en el ejemplo anterior que maneja la señal.

 const express = require ( 'express' ); const mongoose = require ( 'mongoose' ); const app = express(); app.use(express.urlencoded({ extended : true })); app.use(express.json()); mongoose.connect( 'mongodb://localhost/test' , (err) => { if (err) throw err; console .log( 'Mongoose connected!' ); }); const User = mongoose.model( 'User' , { name : String }); app.post( '/user' , async (req, res) => { try { const user = new User({ name : req.body.username }); await user.save(); res.send( 'Success!' ).status( 201 ); } catch (err) { res.send(err.message).status( 500 ); } }); app.listen( 3000 , () => console .log( 'Example app listening on port 3000!' )); process.on( 'SIGTERM' , () => { console .info( 'SIGTERM signal received.' ); });

Ahora vamos a probar y probarlo.
Ejecute el servidor, después de eso, necesita obtener el número PID, lo obtiene usando el comando ps, por lo que ahora tiene el número y puede intentar matar el servidor usando este comando kill [PID_number] o simplemente matar todo el nodo que enviará una señal a todos los servidores de nodos, después de ejecutar este comando obtendrá este registro de las salidas de los nodos

Señal SIGTERM recibida.

Si vuelve a intentarlo, obtendrá el mismo registro.

Señal SIGTERM recibida.

-Hmmm, ¿por qué no se ha matado el proceso?

Porque manejaste la señal y la ignoraste.

Entonces, el primer paso está hecho, pasemos al siguiente paso.

2. Detener nuevas solicitudes del cliente

Ahora debemos detener el servidor http y dejar de aceptar nuevas solicitudes.
Se puede hacer usando la función server.close para obtener más información sobre eso, también puede echar un vistazo en NodeJS doc .

Entonces, el código se verá así

 const express = require ( 'express' ); const mongoose = require ( 'mongoose' ); const app = express(); app.use(express.urlencoded({ extended : true })); app.use(express.json()); mongoose.connect( 'mongodb://localhost/test' , (err) => { if (err) throw err; console .log( 'Mongoose connected!' ); }); const User = mongoose.model( 'User' , { name : String }); app.post( '/user' , async (req, res) => { try { const user = new User({ name : req.body.username }); await user.save(); res.send( 'Success!' ).status( 201 ); } catch (err) { res.send(err.message).status( 500 ); } }); const server = app.listen( 3000 , () => console .log( 'Example app listening on port 3000!' )); process.on( 'SIGTERM' , () => { console .info( 'SIGTERM signal received.' ); console .log( 'Closing http server.' ); server.close( () => { console .log( 'Http server closed.' ); }); });

Dejará de aceptar nuevas conexiones al servidor y si intenta llamar al servidor, su solicitud fallará.

3. Cerrar todos los procesos de datos

En este ejemplo, el punto es cerrar la conexión MongoDB porque no quedan solicitudes en la base de datos.

Entonces se puede hacer con este código.

 const express = require ( 'express' ); const mongoose = require ( 'mongoose' ); const app = express(); app.use(express.urlencoded({ extended : true })); app.use(express.json()); mongoose.connect( 'mongodb://localhost/test' , (err) => { if (err) throw err; console .log( 'Mongoose connected!' ); }); const User = mongoose.model( 'User' , { name : String }); app.post( '/user' , async (req, res) => { try { const user = new User({ name : req.body.username }); await user.save(); res.send( 'Success!' ).status( 201 ); } catch (err) { res.send(err.message).status( 500 ); } }); const server = app.listen( 3000 , () => console .log( 'Example app listening on port 3000!' )); process.on( 'SIGTERM' , () => { console .info( 'SIGTERM signal received.' ); console .log( 'Closing http server.' ); server.close( () => { console .log( 'Http server closed.' ); // boolean means [force], see in mongoose doc mongoose.connection.close( false , () => { console .log( 'MongoDb connection closed.' ); }); }); });

-Hmmmmmmm, ¿por qué el servidor de nodos se cierra después de que se cierra la conexión de base de datos?

Es una pregunta muy interesante, puedes tratar de entender ese punto por ti mismo, pero si no puedes o no quieres, lo describiré en el próximo capítulo.

4. Cerrar todos los procesos de datos

Como ha visto, nuestra aplicación cierra la conexión con la base de datos.

¿Cual es la causa? -Evento de bucle

Como sabemos, NodeJS saldrá cuando la cola de EventLoop esté vacía y no quede nada por hacer.

Pero a veces tu aplicación puede tener más funciones y no saldrá automáticamente, en este punto viene nuestro último trabajo por hacer.
Necesitamos salir del proceso usando la función process.exit.

Y el ejemplo final del servidor de apagado elegante se verá así

 const express = require ( 'express' ); const mongoose = require ( 'mongoose' ); const app = express(); app.use(express.urlencoded({ extended : true })); app.use(express.json()); mongoose.connect( 'mongodb://localhost/test' , (err) => { if (err) throw err; console .log( 'Mongoose connected!' ); }); const User = mongoose.model( 'User' , { name : String }); app.post( '/user' , async (req, res) => { try { const user = new User({ name : req.body.username }); await user.save(); res.send( 'Success!' ).status( 201 ); } catch (err) { res.send(err.message).status( 500 ); } }); const server = app.listen( 3000 , () => console .log( 'Example app listening on port 3000!' )); process.on( 'SIGTERM' , () => { console .info( 'SIGTERM signal received.' ); console .log( 'Closing http server.' ); server.close( () => { console .log( 'Http server closed.' ); // boolean means [force], see in mongoose doc mongoose.connection.close( false , () => { console .log( 'MongoDb connection closed.' ); process.exit( 0 ); }); }); });

El argumento 0 significa salir con un código de "éxito" .
Para salir con un código de "falla" use 1.

Para obtener este código de salida después del apagado, ejecute este comando en la terminal donde ejecuta su servidor de nodo.

eco $?

De forma predeterminada, NodeJS sale con el código de proceso 0 si EventLoop está vacío.

¡Eso es todo!

Resumen

No es la única forma de apagar el servidor correctamente, funcionará bien en proyectos pequeños y se puede escribir fácilmente, pero no digo que en proyectos grandes no sea necesario. En proyectos grandes que probablemente usen el equilibrio del servidor (es decir, Nginx), puede equilibrar la carga y no enviar ninguna solicitud a ese servidor y apagarlo.

Gracias por leer este artículo, no dude en hacer cualquier pregunta o enviarme un tweet a @nairhar .

Mi artículo sobre "Comprobaciones de estado de NodeJS y protección contra sobrecarga"
https://medium.com/@nairihar/nodejs-health-checks-and-overload-protection-368a132a725e