2016-03-11 13 views
6

Ich muss meinen Node JS Server, der auf Heroku läuft, in eine Nachrichtenwarteschlangenarchitektur verschieben. Derzeit empfängt der Server eine HTTP-Anforderung, führt eine Verarbeitung durch und antwortet. Das Problem ist, dass die Verarbeitung einige Zeit in Anspruch nimmt, vor allem, wenn es viele Anfragen gibt. Diese lange Verarbeitungszeit führt zu einem Timeout, einer Überlastung und einem Absturz des Servers! Meine Lektüre sagt mir, dass ich einen Hintergrundarbeiter brauche, der die Verarbeitung durchführt.Knoten JS Nachrichtenwarteschlange auf Heroku

Ich habe keine Erfahrung mit Nachrichtenwarteschlangen und Hintergrundarbeiter und ich bin auf der Suche nach einem sehr einfachen Beispiel, um loszulegen. Kann jemand ein einfaches, verständliches Modul oder ein Beispiel vorschlagen, um zu beginnen?

Ich habe einige examples gefunden, aber sie sind komplex und ich verliere mich! Ich möchte ein Barebone-Beispiel, aus dem ich bauen kann.

+0

Cool. Haben Sie eine Vorschau/Probe des Screencasts, den ich sehen kann? Ich möchte sehen, ob dies das richtige Niveau ist. Spielt das, was Sie lehren, auf Heroku zu? – user3320795

+1

eines der Interviews ist kostenlos, hier: https://sub.watchmecode.net/episode/rmq-interviews-udi-dahan/ aber ich habe nicht viel für die Vorschau, sonst ... ich sollte eine Vorschau zusammenstellen video –

Antwort

8

Mal sehen, wie dies mit RabbitMQ zu tun. Zuerst benötigen Sie einen RabbitMQ-Server, mit dem Sie in Ihrer Entwicklungsumgebung arbeiten können. Wenn Sie es nicht bereits haben (Check "sudo Service rabbitmq-Server-Status") Sie (auf Ubuntu oder ähnliches) installieren können, wie folgt: der Server mit Laufen bringen Dann

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 

:

sudo service rabbitmq-server start 

Sie müssen auch einen RabbitMQ-Dienst für Ihre Heroku-Bereitstellung konfigurieren. Verwenden wir CloudAMPQ für dieses Beispiel.

heroku addons:create cloudamqp:lemur 

, dass eine neue CLOUDAMQP_URL Umgebungsvariable in Ihrer Heroku App schaffen: Sie können ihren Free-Plan Ihre Heroku App mit hinzufügen.

Als nächstes benötigen Sie einen geeigneten RabbitMQ-Client für Ihre node.js App. Es gibt ein paar von ihnen gibt, aber für dieses Beispiel wollen wir nutzen ampqlib:

npm install ampqlib --save 

, dass so etwas wie die folgende Zeile in der package.json Abhängigkeiten hinzufügen sollte:

"amqplib": "^0.4.1", 

Das nächste, was Fügen Sie einen Hintergrund "Arbeiter" Dyno zu Ihrer Heroku App hinzu. Ich nehme an, dass Sie derzeit nur einen einzigen Web-Dyno in Ihrer Procfile haben. So , müssen Sie eine weitere Zeile hinzufügen für einen Arbeiter instanziiert wird, wie zum Beispiel:

worker: node myworker.js 

Schließlich müssen Sie den Code schreiben, die Ihre Web dyno ermöglichen mit Ihrem Arbeitnehmer dyno über RabbitMQ zu interagieren.

Für dieses Beispiel nehme ich an, dass Ihr Web-Dyno Nachrichten in einer RabbitMQ-Nachrichtenwarteschlange "veröffentlicht" und Ihr Worker-Dyno diese Nachrichten "konsumiert".

Beginnen wir mit dem Schreiben von Code zum Veröffentlichen in einer Nachrichtenwarteschlange. Dieser Code muss irgendwo in Ihrem Web-dyno laufen:

// 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')); 
} 

Sie müssen rufen createPublisherChannel() bei der Initialisierung des Web-dyno.Rufen Sie dann publishMsg() auf, wenn Sie eine Nachricht an die Warteschlange senden möchten.

Schließlich schreiben wir den Code für den Verzehr der oben genannten Nachricht im Worker-Dyno. So zum Beispiel so etwas wie die folgenden in myworker.js hinzufügen:

// 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); 

Schließlich Test mit "Heroku local". Sie sollten sehen, dass Sie jetzt zwei Prozesse in Ihrer App ausführen, "Web" und "Worker". Wann immer Sie publishMsg() in Ihrem Web-Dyno aufrufen, sollten Sie hoffentlich sehen, wie der Wroker-Dyno den Inhalt der Nachricht an Ihre Konsole ausspuckt. Um zu sehen, was in Ihren RabbitMQ-Warteschlangen vor sich geht, können Sie Folgendes verwenden:

+1

Danke für diese Erklärung, es ist sehr hilfreich. Ich habe jedoch eine Frage .. Wie teile ich diesen PublisherChnl zwischen verschiedenen Modulen? – shagrin

+0

Ich nehme an, Sie meinen, dass Sie in Ihrer Warteschlange Nachrichten aus mehreren node.js-Modulen in Ihrer Web-App veröffentlichen möchten? Wenn ja, exportieren Sie einfach die Funktion publishMsg mit "exports.publishMsg = publishMsg", so dass Sie sie von überall aufrufen können. Wenn das nicht das ist, was du gemeint hast, dann klär bitte klar. –

+0

Ja, das war meine Frage. Danke für die Klarstellung. Ich habe noch einen Zweifel Gedanke .. Ich habe diesen Code ausprobiert und es sieht aus wie startConsuming() ausgeführt wird, bevor createConsumerChannel() fertig ist. Was ist der beste Weg, um es zu verketten? Ich kann es schaffen zu arbeiten startConsuming() direkt nach dem einen 'consumerChnl = ch;' aber ist es der richtige Weg? – shagrin