2016-03-11 26 views
6

Ho bisogno di spostare il mio server Node JS in esecuzione su Heroku su un'architettura di coda messaggi. Attualmente, il server riceve una richiesta HTTP, esegue alcune elaborazioni e risponde. Il problema è che l'elaborazione richiede un po 'di tempo, soprattutto quando ci sono molte richieste. Questo lungo tempo di elaborazione causa il timeout, il sovraccarico e l'arresto anomalo del server! La mia lettura mi dice che è necessario un lavoratore in background per eseguire l'elaborazione.Coda coda messaggi JS su Heroku

Non ho esperienza con code di messaggi e operatori in background e sto cercando un esempio molto semplice per iniziare. Qualcuno può suggerire un modulo o un esempio semplice e comprensibile per iniziare?

Ho trovato alcuni examples ma sono complessi e mi sto perdendo! Voglio un esempio barebone da cui posso costruire.

+0

Freddo. Avete qualche anteprima/campione dello screencast che posso vedere? Voglio vedere se questo è il livello giusto. Ciò che insegni si applica a Heroku? – user3320795

+1

una delle interviste è gratuita, qui: https://sub.watchmecode.net/episode/rmq-interviews-udi-dahan/ ma non ho molto per le anteprime, altrimenti ... dovrei mettere insieme un'anteprima video –

risposta

8

Vediamo come farlo con RabbitMQ. Innanzitutto, è necessario un server RabbitMQ con cui lavorare nel proprio ambiente di sviluppo. Se non lo avete già (controlla "Servizio sudo stato RabbitMQ-server") è possibile installare (su ubuntu o simile) come segue:

sudo su -c "echo 'deb http://www.rabbitmq.com/debian/ testing main' >> /etc/apt/sources.list" 
wget http://www.rabbitmq.com/rabbitmq-signing-key-public.asc 
sudo apt-key add rabbitmq-signing-key-public.asc 
sudo apt-get update 
sudo apt-get install rabbitmq-server 
rm rabbitmq-signing-key-public.asc 

Quindi, ottenere il server che esegue con:

sudo service rabbitmq-server start 

È inoltre necessario configurare un servizio RabbitMQ per la distribuzione di Heroku. Usiamo CloudAMPQ per questo esempio. È possibile aggiungere il suo Piano libero al vostro Heroku app con:

heroku addons:create cloudamqp:lemur 

che creerà una nuova variabile d'ambiente CLOUDAMQP_URL nel vostro Heroku app.

Successivamente, sarà necessario un client RabbitMQ adatto per l'app node.js. ci sono alcuni di loro là fuori, ma per questo esempio, usiamo ampqlib:

npm install ampqlib --save 

Questo dovrebbe aggiungere qualcosa come la seguente riga nel vostro dipendenze package.json:

"amqplib": "^0.4.1", 

La prossima cosa è aggiungere un dyno di sfondo "lavoratore" alla tua app di Heroku. Suppongo che al momento tu abbia un solo dyno Web nel tuo Procfile. Quindi, è necessario aggiungere un'altra linea per istanziare un lavoratore, come ad esempio:

worker: node myworker.js 

Infine, è necessario scrivere il codice che permetterà il vostro Web banco prova per interagire con il banco prova lavoratore tramite RabbitMQ.

Per questo esempio, assumerò che il tuo banco di prova "pubblicherà" i messaggi su una coda di messaggi RabbitMQ e il tuo addetto al lavoro "consumerà" questi messaggi.

Quindi, iniziamo con la scrittura del codice per la pubblicazione in una coda di messaggi. Questo codice deve essere eseguito da qualche parte nel vostro Web banco:

// Define ampq_url to point to CLOUDAMPQ_URL on Heroku, or local RabbitMQ server in dev environment 
var ampq_url = process.env.CLOUDAMQP_URL || "amqp://localhost"; 
var ampq_open = require('amqplib'); 
var publisherChnl; 

function createPublisherChannel() { 

    // Create an AMPQ "connection" 
    ampq_open.connect(ampq_url) 
     .then(function(conn) { 
      // You need to create at least one AMPQ "channel" on your connection 
      var ok = conn.createChannel(); 
      ok = ok.then(function(ch){ 
       publisherChnl = ch; 
       // Now create a queue for the actual messages to be sent to the worker dyno 
       publisherChnl.assertQueue('my-worker-q'); 
      }) 
     }) 
    } 

function publishMsg() { 
    // Send the worker a message 
    publisherChnl.sendToQueue('my-worker-q', new Buffer('Hello world from Web dyno')); 
} 

Sarà necessario chiamare createPublisherChannel() durante l'inizializzazione del Web banco.Quindi, chiama publishMsg() ogni volta che si desidera inviare un messaggio alla coda.

Infine, scriviamo il codice per il consumo del messaggio sopra nel banco prova operaio. Così, ad esempio, aggiungere qualcosa di simile a quanto segue in myworker.js:

// Just like in Web dyno... 
var amqp_url = process.env.CLOUDAMQP_URL || "amqp://localhost"; 
var open_ampq = require('amqplib').connect(amqp_url); 
var consumerChnl;  

// Creates an AMPQ channel for consuming messages on 'my-worker-q' 
function createConsumerChannel() {  
    open_ampq 
     .then(function(conn) { 
      conn.createChannel() 
       .then(function(ch) { 
        ch.assertQueue('my-worker-q'); 
        consumerChnl = ch; 
      }); 
     }); 
} 

function startConsuming() { 
    consumerChnl.consume('my-worker-q', function(msg){ 
     if (msg !== null) { 
      console.log(msg.content.toString()); 
      // Tell RabbitMQ server we have consumed the message 
      consumerChnl.ack(msg); 
     } 
    }) 
} 

createConsumerChnl().then(startConsuming); 

Infine, prova con "Heroku locale". Dovresti vedere che ora hai 2 processi in esecuzione nella tua app, "Web" e "worker". Ogni volta che chiami publishMsg() nel tuo dyno Web, dovresti vedere che il droke wroker sputa il contenuto del messaggio sulla tua console. Per vedere cosa succede nelle code RabbitMQ, è possibile utilizzare:

sudo rabbitmqctl list_queues 
+1

Grazie per questa spiegazione, è molto utile. Ho una domanda però .. come faccio a condividere questo publisherChnl tra diversi moduli? – shagrin

+0

Suppongo che intendi che vuoi essere in grado di pubblicare messaggi sulle code da più moduli node.js nella tua app web? In tal caso, è sufficiente esportare la funzione publishMsg con "exports.publishMsg = publishMsg" in modo da poterla chiamare da qualsiasi luogo. Se non è quello che intendevi, per favore chiarisci. –

+0

Sì, quella era la mia domanda. Grazie per il chiarimento. Ho ancora un dubbio sul pensiero .. Ho provato questo codice e sembra che startConsuming() venga eseguito prima che venga creato createConsumerChannel(). Qual è il modo migliore per metterlo in catena? Posso farlo funzionare chiamando startConsuming() subito dopo uno 'consumerChnl = ch;' ma è la strada giusta? – shagrin