Pratiche di applicazioni web ad alte prestazioni
introdurre
fibjs è un framework di server applicativo ad alte prestazioni progettato principalmente per lo sviluppo di backend web. È basato sul motore JavaScript di Google v8 e sceglie una soluzione di concorrenza diversa rispetto ai callback tradizionali. fibjs utilizza le fibre per isolare la complessità aziendale causata dalle chiamate asincrone a livello di framework, riducendo notevolmente la difficoltà di sviluppo e riducendo i problemi di prestazioni causati dalla frequente elaborazione asincrona nello spazio utente.
La progettazione di fibjs presta grande attenzione alle prestazioni. I moduli IO e HTTP di rete integrati adottano un modello I/O non bloccante basato sugli eventi, in modo che gli sviluppatori possano facilmente implementare applicazioni server ad alta affidabilità. E poiché lo strato inferiore è implementato in C++, fibjs ha prestazioni molto superiori e può facilmente far fronte ad un elevato accesso simultaneo e fornire servizi estremamente stabili e affidabili.
Allo stesso tempo, fibjs supporta anche WebSocket, che è un protocollo di comunicazione full duplex basato sul protocollo TCP, che stabilisce una connessione ininterrotta tra il browser e il server, consente la trasmissione dati bidirezionale in tempo reale e può supportare dati in qualsiasi formato di trasmissione. WebSocket può essere utilizzato per implementare facilmente applicazioni di comunicazione in tempo reale con migliori effetti di comunicazione.
In breve, fibjs non solo enfatizza le alte prestazioni e l'elevata affidabilità, ma fornisce anche funzionalità di comunicazione in tempo reale come WebSocket, un framework molto adatto per lo sviluppo di applicazioni Web ad alta velocità.
Configurazione dell'ambiente di sviluppo
Prima di iniziare lo sviluppo di fibjs, dobbiamo preparare l'ambiente di sviluppo. Questo capitolo introdurrà come installare fibjs, come utilizzare lo strumento fibjs per inizializzare il progetto e come utilizzare l'ambiente di sviluppo integrato IDE.
Installa fibj
Per diversi sistemi operativi, i metodi di installazione di fibj sono leggermente diversi.
Per gli utenti Linux e macOS, fibjs può essere installato utilizzando il seguente comando:
1curl -s https://fibjs.org/download/installer.sh | sh
Se utilizzi macOS e utilizzi il gestore pacchetti Homebrew, puoi anche installarlo utilizzando il seguente comando:
1brew install fibjs
Per gli utenti Windows, è necessario scaricare il programma di installazione dal sito Web ufficiale di fibjs e quindi seguire le istruzioni per installarlo.
Crea un nuovo progetto utilizzando fibjs –init
Dopo aver installato fibjs, puoi utilizzare lo strumento fibjs per creare rapidamente nuovi progetti. Utilizzare il comando seguente per creare un modello di progetto di base:
1fibjs --init
Questo comando creerà una nuova struttura di progetto nella directory corrente, incluso package.json, che viene utilizzato per archiviare le informazioni di base del progetto e le informazioni sulle dipendenze.
Scrittura di applicazioni web
Lo sviluppo di applicazioni Web è attualmente lo scenario applicativo più comunemente utilizzato di fibjs. fibjs fornisce una serie di strumenti e moduli per aiutarci a creare applicazioni Web più rapidamente.
Scrivere un server HTTP
- Per prima cosa importa il modulo http;
- Crea un'istanza di http.Server e ascolta le richieste.
- Il server viene avviato tramite la funzione di avvio.
1
2
3
4
5
6const http = require('http');
const server = new http.Server(8080, (req) => {
req.response.write('Hello World!');
});
server.start();
Analizza i parametri URL e il corpo della richiesta
L'analisi dei parametri URL e dei corpi delle richieste è molto importante e viene utilizzata in varie applicazioni lato server. In fibjs, i parametri dell'URL in entrata possono essere analizzati direttamente tramite req.query e il corpo della richiesta viene letto tramite 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();
Implementare il controllo dell'accesso all'interfaccia
Limitare l'accesso degli utenti tramite le interfacce è uno scenario molto comune. Di seguito è riportato un semplice esempio.
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();
Aggiungi l'elaborazione del routing
Il routing è uno dei concetti più importanti in un'applicazione Web. Il routing si riferisce alla distribuzione delle richieste ricevute ai processori secondo determinate regole. In fibjs, puoi scrivere il tuo modulo di routing e associarlo al server http, quindi eseguire la corrispondenza degli URL e l'elaborazione corrispondente attraverso l'analisi del percorso personalizzata.
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();
L'esempio sopra può anche essere implementato con una sintassi più semplice:
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();
Gestione e registrazione degli errori
In fibjs, puoi acquisire eccezioni logiche tramite blocchi try-catch e inviarle a file di registro per il debug e la registrazione; se si tratta di eccezioni fatali, possono essere inviate direttamente al framework superiore per l'elaborazione.
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);
}
});
richiesta interdominio
In fibjs, possiamo utilizzare il metodo EnableCrossOrigin per consentire richieste tra domini. Ecco un codice di esempio su come creare un server http e consentire richieste tra domini:
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();
Nell'esempio sopra abbiamo creato un server http con porta 8080. Il metodo EnableCrossOrigin() consente richieste multiorigine.
Quando si utilizza EnableCrossOrigin per consentire richieste tra domini, è possibile specificare le intestazioni tra domini che possono essere ricevute passando un parametroallowHeaders. Per impostazione predefinita, il valoreallowHeaders è Content-Type.
Il codice di esempio è il seguente:
1
2// enable "Content-Type" and "Authorization" headers in cross domain request
server.enableCrossOrigin("Content-Type, Authorization");
Nel codice precedente, il valore diallowHeaders è "Content-Type, Authorization", il che significa che al server è consentito ricevere le due intestazioni tra domini "Content-Type" e "Authorization". Se la richiesta contiene altre intestazioni, verrà rifiutata dal server.
Va notato che quando utilizziamo EnableCrossOrigin per impostare la possibilità di ricevere intestazioni tra domini, dobbiamo anche impostare l'intestazione della richiesta corrispondente quando inviamo richieste tra domini, altrimenti verrà rifiutata anche dal server.
WebSocket
Il protocollo WebSocket è un protocollo di comunicazione full-duplex basato sul protocollo TCP. Stabilisce una connessione ininterrotta tra il browser e il server, può realizzare una trasmissione dati bidirezionale in tempo reale e può supportare la trasmissione dati in qualsiasi formato. In fibjs, il modulo di supporto WebSocket fornisce le interfacce API corrispondenti, che possono realizzare lo sviluppo di server e client WebSocket.
Utilizza il modulo WebSocket nativo di fibjs per implementare il lato server WebSocket
Lato server, le richieste HTTP possono essere convertite in connessioni WebSocket tramite la funzione di aggiornamento. Quando crei un oggetto server http, puoi utilizzare ws.upgrade(callback) e passarlo al metodo http.start() per convertire la richiesta http in 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();
Nell'esempio precedente, possiamo monitorare l'evento del messaggio inviato dal client e l'evento di chiusura della connessione tra il server e il client. Quando il server riceve il messaggio del client, invia nuovamente lo stesso messaggio al client. A questo punto viene implementata la semplice comunicazione punto-punto WebSocket.
Implementare l'interazione con l'archiviazione dei dati
Quando si utilizza WebSocket per la comunicazione, oltre al semplice invio e ricezione di messaggi, è necessario considerare anche operazioni come l'archiviazione persistente e l'interrogazione dei dati. Al momento è necessario utilizzare il database. Puoi utilizzare il modulo db integrato in fibjs per interagire con il database.
Il codice di esempio è il seguente:
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();
Nell'esempio precedente, abbiamo prima utilizzato il metodo openMySQL del modulo db per creare un oggetto di connessione al database MySQL mysql, quindi, dopo aver ascoltato il messaggio dal client, abbiamo utilizzato il metodoexe per eseguire direttamente la query SQL e ottenere i record che soddisfare le condizioni. Infine, i risultati della query vengono inviati al client tramite il protocollo WebSocket.
Va notato che nello sviluppo effettivo, la gestione delle eccezioni e la sicurezza dei dati devono essere eseguite bene.
In sintesi, attraverso il modulo db possiamo interagire facilmente e facilmente con il database, abbinato al protocollo WebSocket, per implementare applicazioni Web in tempo reale e performanti.
Implementare la comunicazione client-server WebSocket
Sul lato client, puoi connetterti a un server WebSocket creando un'istanza WebSocket e specificando un URL, quindi inviare messaggi al server.
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);
}
Nel codice client sopra creiamo un'istanza WebSocket e ne specifichiamo l'URL. Dopo aver stabilito con successo la connessione, possiamo inviare messaggi al server. Quando il server riceve il messaggio del client, invia nuovamente lo stesso messaggio al client. A questo punto viene implementata la semplice comunicazione punto a punto WebSocket.
Vantaggi e scenari di utilizzo di WebSocket
Il protocollo WebSocket ha un tipico modello di comunicazione bidirezionale, che consente al server di inviare attivamente i dati al client, ed è spesso utilizzato per implementare chat, giochi online e altre occasioni che richiedono tempo reale ed elevata immediatezza. Rispetto ad altri protocolli di trasmissione, il protocollo WebSocket presenta i seguenti vantaggi:
• Elevate prestazioni in tempo reale, supporta la comunicazione bidirezionale • Specifica del protocollo semplice, facile da usare • In grado di gestire un gran numero di connessioni simultanee • Supporta connessioni lunghe, riducendo il tempo di trasmissione della rete
Gli scenari di utilizzo più comuni di WebSocket includono chat Web, battaglie di gioco, riproduzione online e messaggistica istantanea.
In sintesi, attraverso il modulo di supporto WebSocket, è molto semplice da implementare e gli sviluppatori possono creare rapidamente le proprie applicazioni Web.
prova unitaria
Strutture di test e metodi di test
Nel processo di sviluppo del software, il test è un collegamento molto importante e lo unit test ne è una parte importante. Il test unitario può verificare efficacemente se il codice soddisfa la progettazione e i requisiti ed evitare errori introdotti quando il codice viene modificato. In generale, il principio del test unitario è testare ogni funzione e metodo per garantire che l'input e l'output di ciascuna funzione e metodo siano corretti.
Un framework di test è una base di codice utilizzata per scrivere, eseguire e verificare i casi di test. Fornisce funzioni di gestione, esecuzione e reporting dei casi di test. In JavaScript e Node.js, i framework di test unitari più diffusi includono Mocha, Jest e Jasmine. In fibjs abbiamo anche il nostro framework di test, il modulo test.
Nel processo di test unitario, i metodi di test comunemente utilizzati includono il test della scatola nera e il test della scatola bianca.
Il test della scatola nera è un metodo di test che considera solo l'input e l'output di una funzione senza considerare i dettagli di implementazione all'interno della funzione. Il test della scatola nera si basa sull'analisi dei requisiti e sulle specifiche di progettazione. Attraverso l'analisi e l'esecuzione dei test case, determina se il programma presenta errori logici, errori di confine, problemi di sicurezza, ecc. Il vantaggio è che il processo di test è semplice e i risultati sono affidabili, lo svantaggio è che il test non può coprire tutti i percorsi del programma.
Il test white-box è un metodo di test che considera i dettagli di implementazione interna di una funzione, comprese le istruzioni condizionali, le istruzioni di loop, la ricorsione e la copertura del codice. Questi test possono identificare possibili problemi nell'interazione tra dati condivisi e codice. Il vantaggio del test white-box è che può coprire tutti i percorsi del programma, lo svantaggio è che il processo di test è più complicato e i risultati del test sono influenzati dall’ambiente e dai metodi di implementazione.
Scrivi casi di test utilizzando il modulo di test
In fibjs possiamo utilizzare il modulo test per scrivere casi di test per il server web. Ecco un semplice esempio:
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();
In questo esempio, utilizziamo le funzioni description e it per definire rispettivamente il modulo di test e il test case e utilizziamo la funzione assert per la verifica dell'asserzione.
Nella funzione di descrizione, possiamo definire più funzioni IT per testare rispettivamente diversi scenari. In ciascuna funzione it, possiamo utilizzare la funzione http.get per simulare una richiesta HTTP GET, ottenere la risposta alla richiesta ed eseguire la verifica delle asserzioni come assertTrue e assertEqual.
Scrivendo casi di test, puoi testare efficacemente la correttezza di funzioni e moduli, garantire la qualità del prodotto e anche migliorare la manutenibilità del codice.
Aggiornamento caldo
L'aggiornamento a caldo si riferisce all'aggiornamento del codice del server senza interrompere il servizio. Durante il processo di sviluppo del programma, per poter iterare rapidamente, sono spesso necessarie modifiche al codice e nuove funzioni. Utilizzando l'aggiornamento a caldo, è possibile utilizzare il nuovo codice per completare il lavoro di iterazione in modo più efficiente senza interrompere il servizio.
In fibjs, possiamo utilizzare il modulo SandBox per ottenere aggiornamenti rapidi e fluidi. Il modulo SandBox può fornire un ambiente di esecuzione sicuro e simulare variabili globali e altre funzioni. Per l'implementazione specifica, fare riferimento ai seguenti passaggi:
- Carica i file di codice che devono essere aggiornati (come web.js).
- Tramite SandBox, crea un nuovo modulo di sicurezza, carica web.js nel modulo e genera il modulo di sicurezza. Rimontare il gestore del servizio in esecuzione tramite il modulo di sicurezza generato.
- Il server continua a elaborare le richieste precedenti e le nuove richieste verranno montate sul nuovo gestore.
Di seguito è riportato un codice di esempio che utilizza il modulo SandBox per implementare aggiornamenti rapidi e fluidi:
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.`);
});
In questo codice, innanzitutto carichiamo il codice in web.js all'avvio del programma, quindi creiamo un'istanza SandBox e carichiamo il codice nell'istanza. Successivamente, abbiamo creato un nuovo server HTTP e utilizzato i metodi nel gestore per elaborare la richiesta.
Nel codice utilizziamo fs.watch per monitorare le modifiche nel file web.js. Una volta modificato il file, ricarichiamo il codice e aggiorniamo l'implementazione nel gestore.
Ottimizzazione delle prestazioni
Durante il processo di sviluppo, spesso dobbiamo affrontare problemi di prestazioni. L'ottimizzazione del codice e il miglioramento delle prestazioni sono una delle competenze essenziali per gli sviluppatori. In fibjs possiamo utilizzare CPU Profiler per aiutarci ad analizzare lo stato di esecuzione del programma e ottimizzare il codice.
In fibjs, devi solo utilizzare il parametro della riga di comando --prof per avviare fibjs per avviare CPU Profiler (l'intervallo predefinito è 1000 ms). Se è necessaria un'analisi del registro con maggiore precisione, è possibile utilizzare il parametro --prof-interval per impostare l'intervallo del registro. Per esempio:
1
2$ fibjs --prof test.js # 启动 CPU Profiler,默认以 1000ms 为间隔
$ fibjs --prof --prof-interval=10ms test.js # 启动 CPU Profiler,以 10000us(即 10ms)为间隔
Al termine dell'esecuzione di fibjs, nella directory corrente verrà generata una directory con il nome del file sorgente, che contiene un file di registro e alcuni file ausiliari. Il nome predefinito del file di registro è fibjs-xxxx.log, dove xxxx è un timestamp. È possibile utilizzare l'opzione --log per specificare il nome del file di registro. A questo punto è possibile --prof-process
elaborare i log generati utilizzando:
1fibjs --prof-process fibjs-xxxx.log prof.svg
Una volta completata l'operazione, utilizzare il browser per aprire prof.svg per visualizzare il grafico della fiamma di questo registro: è possibile fare clic per visualizzare l'immagine a dimensione intera. Nell'immagine a dimensione intera è possibile agire con il mouse per visualizzare più dettagli informazioni: prof.svg .
Nel grafico della fiamma generato, ogni blocco di colore rappresenta un punto di registrazione. Più lungo è il blocco di colore, più volte viene registrato; ogni riga rappresenta uno strato di stack di chiamate, e più strati, più strati vengono chiamati; chiama lo stack viene posizionato sottosopra.Più basso è il blocco colore, più originale è la funzione.
Esistono due tipi di blocchi di colore, uno è rosso e l'altro è blu. Nel profiler fibjs, il rosso rappresenta le operazioni JavaScript e il blu rappresenta le operazioni io o le operazioni native. A seconda del problema che devi risolvere, le aree su cui concentrarti varieranno. Ad esempio, se devi risolvere il problema dell'elevato utilizzo della CPU, devi prestare attenzione ai blocchi di colore rosso. Se la tua applicazione ha un basso utilizzo della CPU ma una risposta lenta, devi prestare attenzione ai blocchi di colore blu. Più grande è il blocco colore nella parte superiore, maggiore sarà l'attenzione e l'ottimizzazione di cui avrà bisogno.
Possiamo provare a regolare le funzioni che occupano più risorse della CPU, implementare IO ecc. in modo asincrono o ottimizzare il codice durante la scrittura.
Distribuzione e on-line
Affinché il nostro progetto possa essere eseguito in un ambiente di produzione, dobbiamo compilarlo e distribuirlo. Qui presentiamo come utilizzare il file package.json per configurare la compilazione e la distribuzione.
Nel progetto, possiamo utilizzare package.json per gestire le dipendenze del progetto, configurare la compilazione e la distribuzione. Prendiamo come esempio un semplice pacchetto package.json:
1
2
3
4
5
6
7{
"name": "my-project",
"version": "1.0.0",
"dependencies": {
"fib-pool": "^1.0.0"
}
}
Quando dobbiamo compilare e distribuire il progetto, dobbiamo solo inserire la directory del progetto nel terminale ed eseguire il seguente comando:
1fibjs --install
Questo comando installerà automaticamente i moduli da cui dipende il progetto. Successivamente, possiamo avviare il progetto utilizzando il seguente comando:
1fibjs app.js