Prácticas de aplicaciones web de alto rendimiento
introducir
fibjs es un marco de servidor de aplicaciones de alto rendimiento diseñado principalmente para el desarrollo de back-end web. Está construido sobre la base del motor JavaScript de Google v8 y elige una solución de concurrencia diferente de la devolución de llamada tradicional. fibjs utiliza fibras para aislar la complejidad comercial causada por las llamadas asincrónicas en la capa del marco, lo que reduce en gran medida la dificultad del desarrollo y reduce los problemas de rendimiento causados por el procesamiento asincrónico frecuente en el espacio del usuario.
El diseño de fibjs presta gran atención al rendimiento. Los módulos de red IO y HTTP incorporados adoptan un modelo de E/S sin bloqueo basado en eventos, y los desarrolladores pueden implementar fácilmente aplicaciones de servidor de alta confiabilidad. Y debido a que la capa inferior está implementada por C ++, fibjs tiene un rendimiento excelente, puede manejar fácilmente un alto acceso concurrente y proporcionar servicios extremadamente estables y confiables.
Al mismo tiempo, fibjs también es compatible con WebSocket, que es un protocolo de comunicación full-duplex basado en el protocolo TCP.Establece una conexión ininterrumpida entre el navegador y el servidor, que puede realizar una transmisión de datos bidireccional en tiempo real y puede Admite transmisión de datos en cualquier formato. Las aplicaciones de comunicación en tiempo real con mejores efectos de comunicación se pueden realizar fácilmente mediante el uso de WebSocket.
En resumen, fibjs no solo enfatiza el alto rendimiento y la alta confiabilidad, sino que también proporciona funciones de comunicación en tiempo real como WebSocket, un marco muy adecuado para desarrollar aplicaciones web de alta velocidad.
Crear el entorno de desarrollo
Antes de comenzar el desarrollo de fibjs, primero debemos preparar el entorno de desarrollo. Este capítulo presentará cómo instalar fibjs, cómo usar la herramienta fibjs para inicializar el proyecto y cómo usar el entorno de desarrollo integrado IDE.
Instalar fibjs
Para diferentes sistemas operativos, la forma de instalar fibjs también es ligeramente diferente.
Para usuarios de Linux y macOS, fibjs se puede instalar con el siguiente comando:
1curl -s https://fibjs.org/download/installer.sh | sh
Si usa macOS y usa el administrador de paquetes Homebrew, también puede instalarlo con:
1brew install fibjs
Para usuarios de Windows, debe descargar el instalador del sitio web oficial de fibjs y luego seguir las instrucciones para instalarlo.
Crea un nuevo proyecto con fibjs --init
Después de instalar fibjs, puede usar la herramienta fibjs para crear rápidamente nuevos proyectos. Se puede crear una plantilla de proyecto básica con el siguiente comando:
1fibjs --init
Este comando creará una nueva estructura de proyecto en el directorio actual, incluido package.json, que se utiliza para almacenar la información básica del proyecto y la información de dependencia.
Escribir una aplicación web
El desarrollo de aplicaciones web es actualmente el escenario de aplicación más utilizado para fibjs. fibjs proporciona una serie de herramientas y módulos para ayudarnos a construir aplicaciones web más rápidamente.
Escribir un servidor HTTP
- Primero importe el módulo http;
- Cree una instancia de http.Server y escuche las solicitudes.
- El servidor se inicia con la función de inicio.
1
2
3
4
5
6const http = require('http');
const server = new http.Server(8080, (req) => {
req.response.write('Hello World!');
});
server.start();
Analizar los parámetros de URL y el cuerpo de la solicitud
Analizar los parámetros de URL y el cuerpo de la solicitud es muy importante y se aplica a varias aplicaciones de servidor. En fibjs, los parámetros de URL entrantes se pueden analizar directamente a través de req.query, mientras que el cuerpo de la solicitud se lee a través de req.body.
1
2
3
4
5
6
7
8const http = require('http');
const server = new http.Server(8080, (req) => {
var name = req.query.get('name');
var msg = name ? `Hello ${name}!` : 'Hello world!';
req.response.write(msg);
});
server.start();
Implementar control de acceso a la interfaz
Restringir el acceso de los usuarios a través de las interfaces es un escenario muy común. A continuación se muestra un ejemplo sencillo.
1
2
3
4
5
6
7
8
9
10const http = require('http');
const server = new http.Server(8080, (req) => {
if (req.headers.get('auth') === 'ok') {
req.response.write('Hello World!');
} else {
req.response.write('Access Denied!');
}
});
server.start();
Agregar procesamiento de enrutamiento
El enrutamiento es uno de los conceptos más importantes en una aplicación web.El enrutamiento se refiere a la distribución de las solicitudes recibidas a los procesadores de acuerdo con ciertas reglas. En fibjs, puede escribir su propio módulo de enrutamiento y vincularlo al servidor http, y luego realizar la coincidencia de URL y el procesamiento correspondiente a través del análisis de enrutamiento personalizado.
1
2
3
4
5
6
7
8
9const http = require('http');
const { Router } = require('mq');
var router = new Router();
router.get('/hello/:name', function (req, name) {
req.response.write('hello, ' + name);
});
var svr = new http.Server(8080, router);
svr.start();
El ejemplo anterior también se puede implementar con una sintaxis más simple:
1
2
3
4
5
6
7
8const http = require('http');
var svr = new http.Server(8080, {
'/hello/:name': function (req, name) {
req.response.write('hello, ' + name);
}
});
svr.start();
Gestión y registro de errores
En fibjs, puede capturar excepciones lógicas a través del bloque try-catch y enviarlas al archivo de registro para el registro de depuración; si es una excepción fatal, puede enviarla directamente al marco superior para su procesamiento.
1
2
3
4
5
6
7
8
9
10const console = require('console');
const http = require('http');
const server = new http.Server(8080, (req) => {
try {
// ...
} catch (e) {
console.log(e.message, e.stack);
}
});
solicitud de dominio cruzado
En fibjs, podemos usar el método enableCrossOrigin para permitir solicitudes de origen cruzado. El siguiente es un código de muestra de cómo crear un servidor http y permitir solicitudes de origen cruzado:
1
2
3
4
5
6
7
8const http = require('http');
const server = new http.Server(8080, (req) => {
req.response.write('Hello World!');
});
server.enableCrossOrigin(); // enable cross domain request
server.start();
En el ejemplo anterior, creamos un servidor http en el puerto 8080. El método enableCrossOrigin() permite solicitudes de origen cruzado.
Al usar enableCrossOrigin para permitir solicitudes de origen cruzado, puede pasar un parámetro allowHeaders para especificar los encabezados de origen cruzado que se pueden recibir. De forma predeterminada, el valor de allowHeaders es Content-Type.
El código de ejemplo es el siguiente:
1
2// enable "Content-Type" and "Authorization" headers in cross domain request
server.enableCrossOrigin("Content-Type, Authorization");
En el código anterior, el valor de allowHeaders es "Tipo de contenido, Autorización", lo que indica que el servidor permite recibir los dos encabezados de dominio cruzado de "Tipo de contenido" y "Autorización". Si la solicitud contiene otros encabezados, el servidor la rechazará.
Cabe señalar que cuando usamos la configuración enableCrossOrigin para permitir la recepción de encabezados entre dominios, también debemos establecer el encabezado de solicitud correspondiente al enviar solicitudes entre dominios, de lo contrario, también será rechazado por el servidor.
WebSocket
El protocolo WebSocket es un protocolo de comunicación full-duplex basado en el protocolo TCP. Establece una conexión ininterrumpida entre el navegador y el servidor, puede realizar la transmisión de datos bidireccional en tiempo real y puede admitir la transmisión de datos en cualquier formato. En fibjs, el módulo de soporte WebSocket proporciona la interfaz API correspondiente, que puede realizar el desarrollo del servidor y cliente WebSocket.
Use el módulo WebSocket nativo de fibjs para implementar el lado del servidor WebSocket
En el lado del servidor, las solicitudes HTTP se pueden convertir en conexiones WebSocket a través de la función de actualización. Al crear un objeto de servidor http, puede usar ws.upgrade(callback) y pasarlo al método http.start() para convertir la solicitud http en WebSocket.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27var ws = require('ws');
var http = require('http');
var server = new http.Server(8080, {
'/ws': ws.upgrade(function(conn, req) {
console.log('a client connected.');
// listening for message events
conn.onmessage = function(evt) {
console.log('received message: ', evt.data);
// echo the message back to client
conn.send('Server: ' + evt.data);
};
// listening for close events
conn.onclose = function(code, reason) {
console.log('closed.');
};
// listening for error events
conn.onerror = function(err) {
console.log(err);
};
})
});
server.start();
En el ejemplo anterior, podemos monitorear el evento de mensaje enviado por el cliente y el evento de cierre de conexión entre el servidor y el cliente. Cuando el servidor recibe un mensaje del cliente, envía el mismo mensaje al cliente. En este punto, se realiza una comunicación simple de punto a punto de WebSocket.
Implementación de interacción con almacenes de datos
Al usar WebSocket para la comunicación, además del envío y la recepción de mensajes simples, también se deben considerar operaciones como el almacenamiento persistente y la consulta de datos. En este momento, necesita usar la base de datos y puede usar el módulo db integrado de fibjs para interactuar con la base de datos.
El código de ejemplo es el siguiente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33var ws = require("ws");
var http = require("http");
var db = require("db");
// open a mysql connection
var mysql = db.openMySQL("mysql://root:password@localhost/dbname");
var server = new http.Server(8080, {
"/ws": ws.upgrade(function(conn, req) {
console.log("a client connected.");
// listening for message events
conn.onmessage = function(evt) {
console.log("received message: ", evt.data);
// use execute to query the data
var rs = mysql.execute("SELECT * FROM user WHERE name=?", evt.data.toString());
conn.send(JSON.stringify(rs));
};
// listening for close events
conn.onclose = function(code, reason) {
console.log("closed.");
};
// listening for error events
conn.onerror = function(err) {
console.log(err);
};
})
});
server.start();
En el ejemplo anterior, primero usamos el método openMySQL del módulo db para crear un objeto de conexión de base de datos MySQL mysql, y luego usamos el método de ejecución para ejecutar consultas SQL directamente después de escuchar los mensajes del cliente para obtener registros que cumplan con las condiciones. Finalmente, los resultados de la consulta se devuelven al cliente a través del protocolo WebSocket.
Cabe señalar que en el desarrollo real, el manejo de excepciones y la seguridad de los datos deben hacerse bien.
En resumen, a través del módulo db podemos interactuar fácilmente con la base de datos, combinado con el protocolo WebSocket, para lograr aplicaciones Web en tiempo real y de alto rendimiento.
Implementar la comunicación cliente-servidor WebSocket
En el lado del cliente, puede conectarse a un servidor WebSocket creando una instancia de WebSocket y especificando una URL, y luego enviar mensajes al servidor.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24var ws = require('ws');
// create a WebSocket object and connect to ws://localhost:8080/ws
var conn = new ws.Socket('ws://localhost:8080/ws');
// listening for open events
conn.onopen = function() {
conn.send('hello');
}
// listening for message events
conn.onmessage = function(evt) {
console.log('received message:', evt.data);
}
// listening for close events
conn.onclose = function(code, reason) {
console.log('closed.');
}
// listening for error events
conn.onerror = function(err) {
console.log(err);
}
En el código de cliente anterior, creamos una instancia de WebSocket y especificamos su URL. Después de que la conexión se haya establecido correctamente, podemos enviar mensajes al servidor. Cuando el servidor recibe un mensaje del cliente, envía el mismo mensaje al cliente. En este punto, se realiza una comunicación simple de punto a punto de WebSocket.
Ventajas y escenarios de uso de WebSocket
El protocolo WebSocket tiene un modelo de comunicación bidireccional típico, lo que permite que el servidor envíe datos de forma activa al cliente y, a menudo, se usa para implementar chats, juegos en línea y otras ocasiones que requieren tiempo real y gran inmediatez. Comparado con otros protocolos de transporte, el protocolo WebSocket tiene las siguientes ventajas:
• Alto rendimiento en tiempo real, admite comunicación bidireccional • Especificación de protocolo simple, fácil de usar • Capaz de manejar una gran cantidad de conexiones simultáneas • Admite conexiones largas, lo que reduce el consumo de tiempo de transmisión de red
Los escenarios de uso más comunes de WebSocket incluyen chat web, batallas de juegos, juegos en línea y mensajería instantánea, etc.
En resumen, con el módulo de soporte de WebSocket, es muy simple de implementar y los desarrolladores pueden crear rápidamente sus propias aplicaciones web.
prueba de unidad
Marco de prueba y método de prueba
En el proceso de desarrollo de software, las pruebas son un vínculo muy importante y las pruebas unitarias son una parte importante de este. Las pruebas unitarias pueden verificar efectivamente si el código cumple con el diseño y los requisitos, y evitar errores introducidos cuando se modifica el código. En general, el principio de las pruebas unitarias es probar cada función y método para garantizar que la entrada y la salida de cada función y método sean correctas.
Un marco de prueba es una base de código para escribir, ejecutar y verificar casos de prueba, que proporciona funciones como la gestión, ejecución e informes de casos de prueba. En JavaScript y Node.js, los marcos de prueba de unidades populares incluyen Mocha, Jest y Jasmine, entre otros. En fibjs, también tenemos nuestro propio marco de prueba, el módulo de prueba.
En el proceso de prueba unitaria, los métodos de prueba generalmente utilizados incluyen pruebas de caja negra y pruebas de caja blanca.
La prueba de caja negra es un método de prueba que solo considera la entrada y la salida de una función sin considerar los detalles de implementación dentro de la función. Las pruebas de caja negra se basan en el análisis de requisitos y las especificaciones de diseño, a través del análisis y la ejecución de casos de prueba, para determinar si hay errores lógicos, errores de límites y problemas de seguridad en el programa. Su ventaja es que el proceso de prueba es simple y los resultados de la prueba son confiables, pero la desventaja es que la prueba no puede cubrir todas las rutas del programa.
La prueba de caja blanca es un método de prueba que considera los detalles de implementación interna de las funciones, incluidas las declaraciones condicionales, las declaraciones de bucle, la recursividad y la cobertura de código. A través de estas pruebas se pueden encontrar posibles problemas en la interacción entre los datos compartidos y el código. La ventaja de la prueba de caja blanca es que puede cubrir todas las rutas del programa. La desventaja es que el proceso de prueba es más complicado y los resultados de la prueba se ven afectados por el entorno y los métodos de implementación.
Escribir casos de prueba usando el módulo de prueba
En fibjs, podemos usar el módulo de prueba para escribir casos de prueba para el servidor web. Aquí hay un ejemplo simple:
1
2
3
4
5
6
7
8
9
10
11
12
13
14var test = require('test');
test.setup();
var http = require('http');
describe('Web server test', () => {
it('should return hello world', () => {
var r = http.get('http://localhost:8080/hello');
assert.equal(r.statusCode, 200);
assert.equal(r.data.toString(), 'Hello World');
});
});
test.run();
En este ejemplo, usamos las funciones describe e it para definir el módulo de prueba y los casos de prueba respectivamente, y usamos la función de afirmación para la verificación de afirmaciones.
En la función de descripción, podemos definir múltiples funciones para probar diferentes escenarios respectivamente. En cada función de TI, podemos usar la función http.get para simular una solicitud HTTP GET, obtener la respuesta de la solicitud y realizar una verificación de aserción como assertTrue y assertEqual.
Al escribir casos de prueba, puede probar de manera efectiva la corrección de funciones y módulos, garantizar la calidad del producto y mejorar la capacidad de mantenimiento del código.
actualización caliente
La actualización en caliente se refiere a actualizar el código del servidor sin detener el servicio. En el proceso de desarrollo del programa, para iterar rápidamente, a menudo es necesario ajustar el código y agregar nuevas funciones. El uso de la actualización en caliente puede usar código nuevo para completar el trabajo de iteración de manera más eficiente sin detener el servicio.
En fibjs, podemos usar el módulo SandBox para lograr actualizaciones en caliente sin problemas. El módulo SandBox puede proporcionar un entorno de ejecución seguro, simular variables globales y otras funciones. Para una implementación específica, consulte los siguientes pasos:
- Cargue los archivos de código que deben actualizarse (como web.js).
- A través de SandBox, cree un nuevo módulo de seguridad, cargue web.js en este módulo y genere un módulo de seguridad. Vuelva a montar el controlador del servicio en ejecución a través del módulo de seguridad generado.
- El servidor continúa procesando las solicitudes anteriores y las nuevas solicitudes se montarán en el nuevo controlador.
Aquí hay un código de muestra para actualizaciones en caliente sin problemas usando el módulo SandBox:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16const fs = require('fs');
const http = require('http');
const { SandBox } = require('vm');
let FILE_PATH = './web.js';
let handler = new SandBox().require(FILE_PATH).handler;
const server = new http.Server(8080, handler);
server.start();
fs.watch(FILE_PATH, (event, filename) => {
handler = new SandBox().require(FILE_PATH).handler;
server.handler = handler;
console.log(`[${new Date().toLocaleString()}] server reloaded.`);
});
En este código, primero cargamos el código en web.js una vez que se inicia el programa, luego creamos una instancia de SandBox y cargamos el código en la instancia. Luego, creamos un nuevo servidor HTTP y usamos el método en el controlador para procesar la solicitud.
En el código, usamos fs.watch para monitorear los cambios del archivo web.js Una vez que el archivo cambia, recargamos el código y actualizamos la implementación en el controlador.
optimización del rendimiento
En el proceso de desarrollo, a menudo necesitamos enfrentar problemas de rendimiento. Optimizar el código y mejorar el rendimiento es una de las habilidades necesarias para los desarrolladores. En fibjs, podemos usar CPU Profiler para ayudarnos a analizar el estado de ejecución del programa y optimizar el código.
En fibjs, solo necesita usar el parámetro de línea de comando --prof para iniciar fibjs, es decir, puede iniciar CPU Profiler (el intervalo predeterminado es 1000ms). Si necesita registros de análisis de mayor precisión, puede usar el parámetro --prof-interval para establecer el intervalo de registro. Por ejemplo:
1
2$ fibjs --prof test.js # 启动 CPU Profiler,默认以 1000ms 为间隔
$ fibjs --prof --prof-interval=10ms test.js # 启动 CPU Profiler,以 10000us(即 10ms)为间隔
Cuando fibjs termine de ejecutarse, se generará un directorio con el nombre del archivo de origen en el directorio actual, que contiene un archivo de registro y algunos archivos auxiliares. El nombre predeterminado del archivo de registro es fibjs-xxxx.log, donde xxxx es una marca de tiempo. El nombre del archivo de registro se puede especificar con la opción --log. En este punto, puede --prof-process
procesar :
1fibjs --prof-process fibjs-xxxx.log prof.svg
Después de ejecutar, abra prof.svg con un navegador para ver el gráfico de llama de este registro:
puede hacer clic para ver la imagen a tamaño completo y, en la imagen a tamaño completo, puede usar el mouse para ver información más detallada: prof .svg _
En el diagrama de llama generado, cada bloque de color representa un punto de grabación. Cuanto más largo sea el bloque de color, más veces se ha grabado; cada línea representa una capa de pila de llamadas, y cuantas más capas, más capas de llamadas; la disposición de la pila está invertida, cuanto más bajo sea el bloque de color, mayor será la función original.
Hay dos tipos de bloques de color, uno es rojo y el otro es azul. En el generador de perfiles de fibjs, el rojo representa operaciones JavaScript y el azul representa operaciones io u operaciones nativas. Dependiendo del problema que necesite resolver, las áreas en las que debe concentrarse variarán. Por ejemplo, si necesita resolver el problema del uso elevado de la CPU, debe prestar atención al bloque de color rojo en este momento; y si su aplicación tiene un uso bajo de la CPU pero una respuesta lenta, debe prestar atención al color azul. bloquear. Cuanto más grande sea el bloque de color cerca de la parte superior, más atención y optimización se necesita.
Podemos intentar ajustar las funciones que consumen más recursos de CPU, implementar IO a través de métodos asíncronos u optimizar el código a la hora de escribir.
implementación en línea
Para que nuestro proyecto se ejecute en producción, necesitamos compilarlo e implementarlo. Aquí presentamos cómo usar el archivo package.json para configurar la compilación y la implementación.
En el proyecto, podemos usar package.json para administrar las dependencias del proyecto, configurar la compilación y la implementación. Tome un paquete de ejemplo simple.json como ejemplo:
1
2
3
4
5
6
7{
"name": "my-project",
"version": "1.0.0",
"dependencies": {
"fib-pool": "^1.0.0"
}
}
Cuando necesitemos compilar e implementar el proyecto, solo necesitamos ingresar al directorio del proyecto en la terminal y luego ejecutar el siguiente comando:
1fibjs --install
Este comando instalará automáticamente los módulos de los que depende el proyecto. Posteriormente, podemos iniciar el proyecto con el siguiente comando:
1fibjs app.js