Ayer un amigo me preguntó cómo implementar el proyecto express. Así que compilé este artículo, que habla principalmente sobre cómo implementar un programa de servidor desarrollado en base a nodejs para referencia de amigos que lo necesitan.
El artículo contiene varias partes:
El proceso (proceso) es la computadora asignación del sistema operativo y la unidad básica de programación de tareas . Abra el administrador de tareas y podrá ver que en realidad hay muchos programas ejecutándose en segundo plano en la computadora y que cada programa es un proceso.
Los navegadores modernos básicamente tienen una arquitectura multiproceso. Tomando el navegador Chrome como ejemplo, abra "Más herramientas" - "Administrador de tareas" y podrá ver la información del proceso del navegador actual. Además, existen procesos de red, procesos de GPU, etc.
La arquitectura multiproceso garantiza un funcionamiento más estable de la aplicación. Tomando el navegador como ejemplo, si todos los programas se ejecutan en un proceso, si hay una falla en la red o un error de representación de la página, todo el navegador fallará. A través de la arquitectura multiproceso, incluso si el proceso de la red falla, no afectará la visualización de las páginas existentes y, en el peor de los casos, no podrá acceder temporalmente a la red.
Un hilo es la unidad más pequeña que el sistema operativo puede realizar en la programación informática . Está incluido en el proceso y es la unidad operativa real en el proceso. Por ejemplo, un programa es como una empresa con múltiples departamentos, que son procesos; la cooperación de cada departamento permite que la empresa funcione normalmente, y los hilos son los empleados, las personas que hacen el trabajo específico.
Todos sabemos que JavaScript es un lenguaje de un solo subproceso. Este diseño se debe a que en los primeros días, JS se usaba principalmente para escribir guiones y era responsable de realizar los efectos interactivos de la página. Si está diseñado como un lenguaje de subprocesos múltiples, en primer lugar, no es necesario y, en segundo lugar, varios subprocesos operan conjuntamente un nodo DOM, entonces, ¿qué consejo debería seguir el navegador? Por supuesto, con el desarrollo de la tecnología, JS ahora también admite subprocesos múltiples, pero solo se usa para manejar cierta lógica no relacionada con las operaciones DOM.
Los subprocesos únicos y los procesos únicos traen un problema grave. Una vez que el subproceso principal de un programa node.js en ejecución se cuelga, el proceso también se colgará y toda la aplicación también se colgará. Además, la mayoría de ordenadores modernos tienen CPU multinúcleo, con cuatro núcleos y ocho subprocesos, y ocho núcleos y dieciséis subprocesos, que son dispositivos muy comunes. Como programa de proceso único, node.js desperdicia el rendimiento de las CPU de múltiples núcleos.
En respuesta a esta situación, necesitamos un modelo multiproceso adecuado para transformar un programa node.js de proceso único en una arquitectura multiproceso.
Hay dos soluciones comunes para implementar una arquitectura multiproceso en Node.js, las cuales utilizan módulos nativos, a saber, el módulo child_process
y el módulo cluster
.
child_process
es un módulo integrado de node.js. Puede adivinar por el nombre que es responsable de las cosas relacionadas con los procesos secundarios.
No entraremos en detalles sobre el uso específico de este módulo. De hecho, solo tiene unos seis o siete métodos, que aún son muy fáciles de entender. Usamos uno de los métodos fork
para demostrar cómo implementar múltiples procesos y la comunicación entre múltiples procesos.
Primero veamos la estructura de directorios del caso de demostración preparado:
Usamos el módulo http
para crear un servidor http. Cuando llega una solicitud /sum
, se creará un proceso hijo a través del módulo child_process
y se notificará al proceso hijo para que realice la lógica de cálculo. También debe escuchar los mensajes enviados por el proceso hijo:
/ /child_process.js constante http = requerir('http') const {bifurcación} = requerir('proceso_niño') servidor constante = http.createServer((req, res) => { if (req.url == '/suma') { // El método fork recibe una ruta de módulo, luego inicia un proceso hijo y ejecuta el módulo en el proceso hijo // childProcess representa el proceso hijo creado let childProcess = fork('./sum.js') //Enviar un mensaje al proceso hijo childProcess.send('El proceso hijo comienza a calcular') // Monitorear los mensajes del proceso hijo en el proceso padre childProcess.on('message', (data) => { res.end(datos + '') }) //Escuche el evento de cierre del proceso hijo childProcess.on('close', () => { // Si el proceso hijo sale normalmente o informa un error y cuelga, irá aquí console.log('el proceso hijo se cierra') childProcess.kill() }) //Escuche el evento de error del proceso hijo childProcess.on('error', () => { console.log('error de proceso hijo') childProcess.kill() }) } if (req.url == '/hola') { res.end('hola') } // Simula el proceso principal para informar un error if (req.url == '/error') { arrojar un nuevo error ('Error del proceso principal') res.end('hola') } }) servidor.escuchar(3000, () => { console.log('El servidor se está ejecutando en 3000') })
sum.js
se utiliza para simular las tareas que realizará el proceso hijo. El proceso hijo escucha los mensajes enviados por el proceso padre, procesa las tareas de cálculo y luego envía los resultados al proceso padre:
// sum.js función obtenerSuma() { sea suma = 0 para (sea i = 0; i < 10000 * 1000 * 100; i++) { suma += 1 } suma de devolución } // el proceso es un objeto global en node.js, que representa el proceso actual. Aquí está el proceso hijo. // Escucha los mensajes enviados por el proceso principal Process.on('message', (data) => { console.log('Mensaje del proceso principal:', datos) resultado constante = obtenerSuma() //Enviar los resultados del cálculo al proceso padre Process.send(resultado) })
Abra la terminal y ejecute el comando node 1.child_process
:
Visita el navegador:
A continuación, simule la situación en la que el proceso hijo informa un error:
// sum.js función obtenerSuma() { // .... } // Después de que el proceso hijo se ejecuta durante 5 segundos, el proceso de simulación cuelga setTimeout(() => { lanzar un nuevo error ('informe de error') }, 1000 * 5) proceso.on('mensaje', (datos) => { //... })
Visite el navegador nuevamente y observe la consola después de 5 segundos:
El proceso hijo ha muerto y luego accede a otra URL: /hello
,
Se puede ver que el proceso padre aún puede manejar la solicitud correctamente, lo que indica que el error informado por el proceso hijo no afectará el funcionamiento del proceso padre .
A continuación, simularemos el escenario en el que el proceso principal informa un error, comentaremos el informe de error simulado del módulo sum.js
, luego reiniciaremos el servicio y accederemos /error
con el navegador:
Después de descubrir que el proceso principal se colgó, todo el programa node.js se cerró automáticamente y el servicio colapsó por completo, sin dejar espacio para la recuperación.
Se puede ver que no es complicado implementar la arquitectura multiproceso de node.js a través fork
de child_process
. La comunicación entre procesos se realiza principalmente a través de send
y on
. A partir de este nombre, también podemos saber que la capa inferior debe ser un modelo de publicación-suscripción.
Pero tiene un problema grave. Aunque el proceso hijo no afecta al proceso padre, una vez que el proceso padre comete un error y cuelga, todos los procesos hijos serán "eliminados de una vez". Por lo tanto, esta solución es adecuada para dividir algunas operaciones complejas y que requieren mucho tiempo en un subproceso separado . Para ser más precisos, este uso se utiliza para reemplazar la implementación de subprocesos múltiples, no de procesamiento múltiple.
utiliza el módulo child_process
para implementar multiproceso, lo que parece inútil. Por lo tanto, generalmente se recomienda utilizar el módulo cluster
para implementar el modelo multiproceso de node.js.
cluster
significa grupo. Creo que todo el mundo está familiarizado con este término. Por ejemplo, en el pasado, la empresa solo tenía una recepción y, a veces, estaba demasiado ocupada para recibir a los visitantes a tiempo. Ahora la empresa ha asignado cuatro mostradores de recepción. Aunque tres de ellos estén ocupados, todavía hay uno que puede recibir nuevos visitantes. Agrupar significa aproximadamente esto: para lo mismo, se asigna razonablemente a diferentes personas para que lo hagan, a fin de garantizar que se pueda hacer mejor.
El uso del módulo cluster
también es relativamente sencillo. Si el proceso actual es el proceso principal, cree una cantidad adecuada de subprocesos según la cantidad de núcleos de CPU y escuche el evento exit
del subproceso. Si un subproceso sale, vuelva a bifurcar el nuevo subproceso. -proceso. Si no es un proceso hijo, se procesa el negocio real.
constante http = requerir('http') grupo constante = requerir('grupo') const cpus = requerir('os').cpus() si (cluster.isMaster) { // Cuando se inicia el programa, primero va aquí y crea múltiples subprocesos según la cantidad de núcleos de CPU para (let i = 0; i < cpus.length; i++) { //Crea un proceso hijo cluster.fork() } // Cuando cualquier proceso hijo cuelga, el módulo del clúster emitirá el evento de 'salida'. En este punto, el proceso se reinicia llamando nuevamente a fork. cluster.on('salir', () => { cluster.fork() }) } demás { // El método fork se ejecuta para crear un proceso hijo y el módulo se ejecutará nuevamente. En este momento, la lógica vendrá aquí const server = http.createServer((req, res) => { consola.log(proceso.pid) res.end('ok') }) servidor.escuchar(3000, () => { console.log('El servidor se está ejecutando en 3000', 'pid: ' + proceso.pid) }) }
Iniciar el servicio:
Como puede ver, el módulo cluster
ha creado muchos procesos secundarios y parece que cada proceso secundario ejecuta el mismo servicio web.
Cabe señalar que estos procesos secundarios no están escuchando en el mismo puerto en este momento. El servidor creado por el método createServer sigue siendo responsable de monitorear el puerto y reenviar solicitudes a cada proceso hijo.
Escribamos un script de solicitud para solicitar el servicio anterior y veamos el efecto.
// solicitud.js constante http = requerir('http') para (sea i = 0; i < 1000; i++) { http.get('http://localhost:3000') }
El módulo http no solo puede crear un servidor http, sino que también puede usarse para enviar solicitudes http. Axios admite entornos de navegador y servidor. En el lado del servidor, el módulo http se utiliza para enviar solicitudes http.
Utilice node
para ejecutar el archivo y observe la consola original:
Se imprimen los ID de proceso de diferentes subprocesos que manejan específicamente la solicitud.
Esta es la arquitectura multiproceso de nodd.js implementada a través del módulo cluster
.
Por supuesto, cuando implementamos proyectos de Node.js, no escribiremos ni usaremos el módulo cluster
tan secamente. Existe una herramienta muy útil llamada PM2 , que es una herramienta de gestión de procesos basada en el módulo de clúster. Su uso básico se presentará en capítulos posteriores.
Hasta ahora, hemos dedicado una parte del artículo a presentar el conocimiento de multiproceso en node.js. De hecho, solo queremos explicar por qué necesitamos usar pm2 para administrar aplicaciones de node.js. Debido al espacio limitado de este artículo y a la falta de una descripción precisa/detallada, este artículo sólo ofrece una breve introducción. Si es la primera vez que entras en contacto con este contenido, es posible que no lo entiendas muy bien, así que no te preocupes, habrá un artículo más detallado más adelante.
Este artículo ha preparado un programa de muestra desarrollado con express, haga clic aquí para acceder.
Implementa principalmente un servicio de interfaz. Al acceder a /api/users
, se utiliza mockjs
para simular 10 datos de usuario y devolver una lista de usuarios. Al mismo tiempo, se iniciará un temporizador para simular una situación de error:
const express = require('express') const Mock = requerir ('mockjs') aplicación constante = expresar() app.get("/api/usuarios", (req, res) => { const lista de usuarios = Mock.mock({ 'lista de usuarios|10': [{ 'identificación|+1': 1, 'nombre': '@cnombre', 'correo electrónico': '@correo electrónico' }] }) setTimeout(()=> { arrojar un nuevo error ('fallo del servidor') }, 5000) estado.res (200) res.json (lista de usuarios) }) aplicación.listen(3000, () => { console.log("Servicio iniciado: 3000") })
Pruébelo localmente y ejecute el comando en la terminal:
node server.js
Abra el navegador y acceda a la interfaz de lista de usuarios:
Después de cinco segundos, el servidor se bloqueará:
Podemos resolver este problema más adelante cuando usemos pm2 para administrar aplicaciones.
Por lo general, después de completar un proyecto vue/react, primero lo empaquetaremos y luego lo publicaremos. De hecho, los proyectos front-end deben empaquetarse principalmente porque el entorno de ejecución final del programa es el navegador, y el navegador tiene varios problemas de compatibilidad y rendimiento, como:
.vue
, .jsx
, .ts
deben compilarseLos proyectos desarrollados con express.js o koa.js no tienen. estos problemas. Además, Node.js adopta la especificación modular CommonJS y tiene un mecanismo de almacenamiento en caché; al mismo tiempo, el módulo solo se importará cuando se utilice ; Si lo empaqueta en un archivo, esta ventaja en realidad se desperdicia. Entonces, para proyectos de Node.js, no es necesario empaquetar.
Este artículo utiliza el sistema CentOS como ejemplo para demostrar
Para facilitar el cambio de versiones de nodos, utilizamos nvm para administrar los nodos.
Nvm (Node Version Manager) es la herramienta de gestión de versiones de Node.js. A través de él, el nodo se puede cambiar arbitrariamente entre múltiples versiones, evitando operaciones repetidas de descarga e instalación cuando se requiere cambiar de versión.
El repositorio oficial de Nvm es github.com/nvm-sh/nvm. Debido a que su script de instalación está almacenado en el sitio githubusercontent
, a menudo es inaccesible. Así que creé un nuevo repositorio espejo en gitee, para poder acceder a su script de instalación desde gitee.
Descargue el script de instalación a través del comando curl
y use bash
para ejecutar el script, lo que completará automáticamente la instalación de nvm:
# curl -o- https://gitee.com/hsyq/nvm/raw/master/install.sh | bash
Cuando se completa la instalación Después de eso, abrimos una nueva ventana para usar nvm:
[root@ecs-221238 ~]# nvm -v0.39.1
puede imprimir el número de versión normalmente, lo que indica que nvm se ha instalado correctamente.
Ahora puede usar nvm para instalar y administrar node.
Ver versiones de nodos disponibles:
# nvm ls-remote
Instalar nodo:
# nvm install 18.0.0
Ver versiones de nodos instalados:
[root@ecs-221238 ~]# nvm list -> v18.0.0 predeterminado -> 18.0.0 (-> v18.0.0) iojs -> N/A (predeterminado) inestable -> N/A (predeterminado) nodo -> estable (-> v18.0.0) (predeterminado) estable -> 18.0 (-> v18.0.0) (predeterminado)
Seleccione una versión para usar:
# nvm use 18.0.0
Una cosa a tener en cuenta es que cuando usa nvm en Windows, debe usar derechos de administrador para ejecutar el comando nvm. En CentOS, inicio sesión como usuario root de forma predeterminada, por lo que no hay problema. Si encuentra errores desconocidos al usarlo, puede buscar soluciones o intentar ver si el problema se debe a los permisos.
Al instalar node, npm se instalará automáticamente. Verifique los números de versión de node y npm:
[root@ecs-221238 ~]# node -v v18.0.0 [root@ecs-221238 ~]# npm -v
La fuente de imagen npm predeterminada
en 8.6.0es la dirección oficial:
[root@ecs-221238 ~]# npm config get registro https://registry.npmjs.org/
Cambie a la fuente espejo nacional de Taobao:
[root@ecs-221238 ~]# npm config set registro https://registry.npmmirror.com
En este punto, el servidor ha instalado el nodo The El entorno y npm están configurados.
Hay muchas formas, ya sea descargándolos al servidor desde el repositorio Github/GitLab/Gitee o cargándolos localmente a través de la herramienta ftp. Los pasos son muy simples y no se volverán a demostrar.
El proyecto de demostración se coloca en el directorio /www
:
Generalmente, los servidores en la nube solo abren el puerto 22 para el inicio de sesión remoto. Los puertos de uso común, como 80 y 443, no están abiertos. Además, el proyecto expreso que preparamos pasa por el puerto 3000. Por lo tanto, primero debe ir a la consola del servidor en la nube, buscar el grupo de seguridad, agregar algunas reglas y abrir los puertos 80 y 3000.
Durante la fase de desarrollo del, podemos usar nodemon
para el monitoreo en tiempo real y el reinicio automático para mejorar la eficiencia del desarrollo. En un entorno de producción, es necesario utilizar el gran asesino: PM2.
primero instale pm2 globalmente:
# npm i -g pm2
Ejecute el comando pm2 -v
para verificar si la instalación se realizó correctamente:
[root@ecs-221238 ~]# pm2 -v5.2.0
Cambie al directorio del proyecto e instale las dependencias primero:
cd /www/express-demo npm install
y luego use el comando pm2
para iniciar la aplicación.
pm2 iniciar aplicación.js -i max // O pm2 start server.js -i 2
La aplicación de administración PM2 tiene dos modos: bifurcación y clúster. Al iniciar la aplicación, al utilizar el parámetro -i para especificar el número de instancias, el modo de clúster se activará automáticamente. En este punto, las capacidades de equilibrio de carga están disponibles.
-i: instancia, el número de instancias. Puede escribir un número específico o configurarlo al máximo.
PM2
verificará automáticamente la cantidad de CPU disponibles y luego iniciará tantos procesos como sea posible.
La aplicación ya está iniciada. PM2 administrará la aplicación en forma de proceso demonio. Esta tabla muestra información sobre la aplicación en ejecución, como el estado de ejecución, el uso de la CPU, el uso de la memoria, etc.
Acceda a la interfaz en un navegador local:
El modo de clúster es un modelo de múltiples procesos y múltiples instancias . Cuando llega una solicitud, se asignará a uno de los procesos para su procesamiento. Al igual que el uso del módulo cluster
que hemos visto antes, debido a la protección de pm2, incluso si un proceso muere, el proceso se reiniciará inmediatamente.
Regrese a la terminal del servidor y ejecute el comando pm2 logs
para ver los registros pm2:
Se puede ver que la instancia de la aplicación con ID 1 cuelga y pm2 reiniciará la instancia inmediatamente. Tenga en cuenta que la identificación aquí es la identificación de la instancia de la aplicación, no la identificación del proceso.
En este punto, se completa la implementación simple de un proyecto expreso. Al utilizar la herramienta pm2, básicamente podemos garantizar que nuestro proyecto pueda ejecutarse de manera estable y confiable.
Aquí hay un resumen de algunos comandos comúnmente utilizados de la herramienta pm2 como referencia.
# Modo bifurcación pm2 start app.js --name app # Establece el nombre de la aplicación en app #Modo clúster# Utilice el equilibrio de carga para iniciar 4 procesos pm2 start app.js -i 4 # Iniciará 4 procesos usando el equilibrio de carga, dependiendo de la CPU disponible pm2 iniciar aplicación.js -i 0 # Equivalente al efecto del comando anterior pm2 start app.js -i max # Amplíe la aplicación con 3 procesos adicionales aplicación de escala pm2 +3 # Ampliar o reducir la aplicación a 2 procesos pm2 escala aplicación 2 # Ver el estado de la aplicación # Mostrar el estado de todos los procesos lista pm2 # Imprime la lista de todos los procesos en formato JSON sin formato pm2 jlist # Utilice JSON embellecido para imprimir la lista de todos los procesos pm2 Prettylist # Mostrar toda la información sobre un proceso específico pm2 describe 0 # Utilice el panel para monitorear todos los procesos pm2 monit #Gestión de registros# Muestra todos los registros de aplicaciones pm2 en tiempo real # Mostrar registros de aplicaciones de aplicaciones en tiempo real aplicación de registros pm2 # Utilice el formato json para mostrar registros en tiempo real, no genere registros antiguos, solo genere registros recién generados registros pm2 --json #Gestión de aplicaciones# Detener todos los procesos pm2 detener todos # Reiniciar todos los procesos pm2 reiniciar todos # Detener el proceso con el ID especificado pm2 stop 0 # Reiniciar el proceso con el ID especificado pm2 reiniciar 0 # Eliminar proceso pm2 con ID 0 eliminar 0 # Eliminar todos los procesos pm2 eliminar todo.
Puedes probar cada comando tú mismo para ver el efecto.
Aquí hay una demostración especial del comando monit
, que puede iniciar un panel en la terminal para mostrar el estado de ejecución de la aplicación en tiempo real. Todas las aplicaciones administradas por pm2 se pueden cambiar mediante las flechas hacia arriba y hacia abajo:
PM2 tiene funciones muy poderosas, mucho más que los comandos anteriores. En la implementación de un proyecto real, es posible que también necesite configurar archivos de registro, modo de vigilancia, variables de entorno, etc. Sería muy tedioso escribir comandos a mano cada vez, por lo que pm2 proporciona archivos de configuración para administrar e implementar aplicaciones.
Puede generar un archivo de configuración mediante el siguiente comando:
[root@ecs-221238 express-demo]# pm2 init simple El archivo /www/express-demo/ecosystem.config.js generado
generará un archivo ecosystem.config.js
:
module.exports = { aplicaciones: [{ nombre: "aplicación1", secuencia de comandos: "./app.js" }] }
También puede crear un archivo de configuración usted mismo, como app.config.js
:
const path = require('path') módulo.exportaciones = { // Un archivo de configuración puede administrar múltiples aplicaciones node.js al mismo tiempo // apps es una matriz, cada elemento es la configuración de una aplicación apps: [{ //Nombre de la aplicación: "express-demo", // Script del archivo de entrada de la aplicación: "./server.js", // Hay dos modos para iniciar la aplicación: cluster y fork. El valor predeterminado es fork. exec_mode: 'clúster', // Número de instancias de aplicación para crear instancias: 'max', // Active la supervisión y reinicie automáticamente la aplicación cuando el archivo cambie watch: true, //Ignora los cambios en algunos archivos del directorio. // Dado que el directorio de registro se coloca en la ruta del proyecto, debe ignorarse; de lo contrario, la aplicación generará registros cuando se inicie, PM2 se reiniciará cuando monitoree los cambios. Si se reinicia y genera registros, ingresará un número infinito. bucle ignorar_watch: [ "módulos_nodo", "registros" ], // Ruta de almacenamiento del registro de errores err_file: path.resolve(__dirname, 'logs/error.log'), //Imprimir la ruta de almacenamiento del registro out_file: path.resolve(__dirname, 'logs/out.log'), //Establece el formato de fecha delante de cada registro en el archivo de registro log_date_format: "AAAA-MM-DD HH:mm:ss", }] }
Deje que pm2 use archivos de configuración para administrar aplicaciones de nodo:
pm2 start app.config.js
Ahora las aplicaciones administradas por pm2 colocarán los registros en el directorio del proyecto (el valor predeterminado es en el directorio de instalación de pm2) y pueden monitorear los cambios de archivos. , reinicia automáticamente el servicio.