2015-08-01 13 views
7

Attualmente sto lavorando a un'applicazione stack Node.js utilizzata da più di 25000 persone, stiamo utilizzando il framework Sails.js in particolare e abbiamo MongoDB L'applicazione è in esecuzione su un'istanza EC2 con 30 GB di RAM, il database è in esecuzione su un cluster basato su AWA Mongolab nella stessa zona è l'EC2. Abbiamo anche ottenuto un'istanza di Elastic Cache Redis con 1,5 GB per l'archiviazione.Come deve essere installato uno stack node.js per un'applicazione molto richiesta?

Quindi il problema principale ed enorme che stiamo affrontando è LATENCY. Quando raggiungiamo un picco di utenti simultanei che richiedono l'applicazione, riceviamo più timeout e applicazioni di vele che superano i 7,5 GB di RAM, le richieste HTTP all'API impiegano più di 15 secondi (il che è inaccettabile) e persino le risposte 502 e 504 inviate da nginx.

Posso notare le operazioni di scrittura Mongo come il nostro problema di latenza principale, tuttavia anche le richieste GET richiedono molto tempo quando è presente un picco di domanda. Non riesco ad accedere ai server di produzione, ho solo ottenuto uno strumento di monitoraggio keymetrics da pm2 (che in realtà è ottimo) e avvisi New Relic.

Quindi, mi piacerebbe conoscere una tabella di marcia per far fronte a questi problemi, forse dovrebbero essere offerte informazioni più dettagliate, per ora posso dire che l'applicazione sembra stabile quando non sono presenti molti utenti.

Quali sono i fattori principali e la configurazione da considerare?

Finora so cosa devo fare, ma io non sono sicuro di dettagli o le how.

IMHO:

  1. cache, per quanto possibile.
  2. Ritarda le operazioni di scrittura MongoDB.
  3. Separare i database Mongo con una maggiore richiesta di scrittura.
  4. Virtualizzare?
  5. Ottimizza le impostazioni del nodo.

Sull'ottimizzazione del codice, ho pubblicato un'altra domanda StackOverflow con un esempio di code patterns I'm following.

Quali sono i vostri consigli e opinioni per le applicazioni di produzione?

+0

Sede [questo] (https://www.mongodb.com/blog/post/performance-testing-mongodb-30-part-1-throughput-improvements-measured-ycsb) se si sta utilizzando MongoDB 2.x – robertklep

+0

E a proposito di operazioni di blocco, sincrone o di lunga durata? Clustering per CPU-core? –

+0

@ stdob-- puoi estendere il tuo commento in una risposta, alcuni commenti ed esempi più lunghi sarebbero apprezzati – diegoaguilar

risposta

3

Fondamentalmente la maggior parte dei punti principali sono già presenti nelle risposte. Li riassumerò semplicemente.

Per ottimizzare l'applicazione, è possibile eseguire diverse operazioni.

  1. tenta di spostare modulo node.js-io.js che hanno ancora un po 'meglio le prestazioni e la più recente all'avanguardia aggiornato. (Ma leggi attentamente le caratteristiche sperimentali). O almeno da node.jsv10 a v12. C'erano molte ottimizzazioni delle prestazioni.

  2. Evitare l'uso di funzioni sincrone che utilizzano operazioni di I/O o operano con grandi quantità di dati.

  3. Passare da un processo di nodo al sistema clustering.

  4. Controllare l'applicazione per perdite di memoria. Sto usando memwatch-next per node.js v12 e memwatch per node.js v10

  5. cercare di evitare il salvataggio dei dati per le variabili globali

  6. Usa caching. Per i dati che dovrebbero essere accessibili a livello globale è possibile utilizzare Redis o Memcached è anche un ottimo negozio.

  7. Evitare di utilizzare async con Promises. Entrambe le librerie stanno facendo le stesse cose. Quindi non c'è bisogno di usare entrambi. (L'ho visto nel tuo esempio di codice).

  8. Combinare async.waterfall con async.parallel metodi in cui è possibile eseguire. Ad esempio, se è necessario recuperare alcuni dati da mongo che sono relativi solo all'utente, è possibile recuperare l'utente e in parallelo recuperare tutti gli altri dati necessari.

  9. Se si utilizza sails.js, assicurarsi che sia in modalità production. (Presumo che tu l'abbia già fatto)

  10. Disabilitare tutti i ganci non necessari. Nella maggior parte dei casi l'hook grunt è inutile. E se non hai bisogno di Socket.io nella tua applicazione, disabilitalo usando il file .sailsrc.Qualcosa di simile:

    { "generatori": { "moduli": {} }, "ganci": { "grunt": false, "prese": false } }

Altri hook che possono essere disabilitati sono: i18n, csrf, cors. MA solo se non li usi nel tuo sistema.

  1. Disabilitare la globalizzazione inutile. In config/globals.js. Presumo _, async, services potrebbe essere disabilitato per impostazione predefinita. Solo perché lo Sails.js utilizza la vecchia versione delle librerie lodash e async e le nuove versioni hanno prestazioni molto migliori.

  2. Installare manualmente lodash e async nel progetto Sails.js e utilizzare nuove versioni. (vedi punto 11)

  3. Alcune operazioni di "scrittura su mongo" possono essere eseguite dopo aver restituito il risultato all'utente. Ad esempio: è possibile chiamare il metodo res.view() che invierà la risposta all'utente prima di Model.save(), ma il codice continuerà a essere in esecuzione con tutte le variabili, quindi è possibile salvare i dati su mongo DB. Quindi l'utente non vedrebbe il ritardo durante l'operazione di scrittura.

  4. È possibile utilizzare code come RabbitMQ per eseguire operazioni che richiedono molte risorse. Ad esempio: se è necessario archiviare la raccolta di grandi quantità, è possibile inviarla a RabbitMQ e restituire la risposta all'utente. Quindi gestisci questo messaggio nei dati dell'archivio degli annunci di processo "in background". Inoltre, può aiutarti a ridimensionare la tua applicazione.

3

In primo luogo, assicurarsi di non utilizzare l'I/O sincrono. Se è possibile eseguire su io.js, c'è il flag --trace-sync-io (iojs --trace-sync-io server.js) che avviserà se si utilizza il codice sincrono con il seguente avviso di console: WARNING: Detected use of sync API.

In secondo luogo, scoprire perché l'utilizzo della RAM è così elevato. Se è a causa di molti dati caricati in memoria (analisi XML, grande quantità di dati restituiti da MongoDB, ecc.), Si dovrebbe considerare l'utilizzo di streams. La garbage collection V8 (la VM JavaScript di Google utilizzata in Node.js/io.js) può causare rallentamenti se l'utilizzo della memoria è molto elevato. Altro qui: Node.js Performance Tip of the Week: Managing Garbage Collection e Node.js Performance Tip of the Week: Heap Profiling

In terzo luogo, esperimento con Node.js clustering e MongoDB sharding.

Infine, controllare se si utilizza o può passare a MongoDB 3.x.Abbiamo osservato alcuni miglioramenti significativi delle prestazioni semplicemente aggiornando da 2.x a 3.x.

+0

È solo in esecuzione e eseguirà il debug? – diegoaguilar

+0

@diegoaguilar yes – krl

2

Per MongoDB è possibile utilizzare mongtop per vedere quali database sono contestati, 2.2+ utilizza per blocchi di database, se il database ha scrivere pesante carico di lavoro si legge saranno interessati come MongoDB sta usando writer greedy locks

E per node.js si potrebbe controllare se ci sono qualsiasi tipo di ritardi sull'anello evento che potrebbe spiegare richiesta API ritarda

(function getEventLoopDelay() { 
    var startTime = Date.now(); 
    setTimeout(function() { 
     console.log(Math.max(Date.now() - startTime - 1000, 0)); 
     getEventLoopDelay(); 
    }, 100); 
})(); 
+0

Che cosa dovrei cercare o non cercare quello snippet di codice? – diegoaguilar

+0

qualsiasi valore superiore a 10-20 ms deve indicare che il ciclo di eventi si satura e richiede di essere accodato prima dell'esecuzione – dainis

Problemi correlati