redis

Job Queues mit Redis

Von am 11.05.2014

Wer hat schon einmal ein einfaches E-Mail aus seiner Web-Applikation heraus verschickt? Wahrscheinlich jeder. Denkt beispielsweise an das klassische Registrierungsmail mit dem Bestätigungstoken. Der User Registriert sich und bekommt ein E-Mail in dem ein spezieller Token ist mit dem er sich freischalten kann. Perfekt. Doch was genau kann man hier falsch machen und wieso sollte man sich um Job Queues Gedanken machen?

Queues

Nun, viele Entwickler implementieren diesen E-Mail verschick-Prozess direkt in den ersten Response. Was folgt? Der Response dauert länger. Das Script führt nicht nur die eigenen Operationen durch, rendert das Template, sondern muss zusätzlich eine SMTP Verbindung öffnen und ein E-Mail verschicken. Das kann mit unter zu sehr nervigen Reaktionen bei den Usern führen, vor allem wenn es um keine banale Registrierung sondern eine komplexere Firmen Web-Applikation handelt die beispielsweise E-Mails verschickt wenn man zu einem bestimmten Projekt Issue einen Kommentar abgibt.
Der Schlüssel hier sind Job Queues. Einfach gesagt lagert man spezielle Funktionalitäten absichtlich aus um Responses weiterhin schnell abwickeln zu lassen. Man “ent-blockt” sozusagen das System bzw. den Response und Redis hilft uns hier sehr gut weiter.

Redis

Redis selbst ist ein einfacher Key-Value Store und betitelt sich selbst oft als “data structure server” da in den einzelnen Value Feldern nicht nur gewöhnliche Zeichen gespeichert werden können, sondern auch komplexe hashes, listen oder sortierte sets. Die zwei größten Vorteile von Redis sind 1. die Schnelligkeit (auf Grund des einfachen Aufbaus) und 2. das Event Handling. Man kann beispielsweise auf einen gewissen Kanal horchen und wird informiert falls neue Daten reinkommen und kann dementsprechend reagieren.

Node.js

Node ist ein großer Haufen toller Sachen, aber grob Zusammengefasst ist Node ein Wrapper um die V8 von Google (JavaScript Interpreter) mit zusätzlichen Libraries (z.B. FileSystem, HTTP uvm.) und bringt somit JavaScript auf die Server-Seite. Es gibt bereits viele gute Redis Packages die uns die Verbindung zum Redis Server erleichtern, sucht man jedoch etwas genauer findet man sogar schon Libraries die uns genau unseren Job abnehmen. Eine dieser Libraries ist Kue.

Dig in!

Ich werde nicht darauf eingehen wie man ein Node Projekt startet oder Kue als Dependency integriert, denn das kann man sich auf den jeweiligen Repositories gut dokumentiert ansehen. Wie erstellt man nun asynchrone jobs?
Man bindet zuerst die notwendigen Libraries ein:

// email job module - job-email.js
var kue = require('kue')
  , jobs = kue.createQueue();

Danach erstellt man die Funktion die uns einen Job generiert:

/**
 * email => {
 *   to: "foo@email.com",
 *   from: "bar@email.com",
 *   subject: "Some Subject",
 *   message: "Hello World!"
 * }
 */
function newEmailJob (email) {
  var job = jobs.create('new email', email);
  job
    .on('complete', function () {
      console.log('Email', job.id, 'with subject', job.data.subject, 'is done!');
    })
    .on('failed', function () {
      console.log('Email', job.id, 'with subject', job.data.subject, 'has failed!');
    });
  job.save();
}

Zuerst wird die Funktions `jobs.create()` verwendet um einen Job zu generieren. Dabei werden Typ des Jobs und ein Objekt mit den Daten übergeben. Als nächstes werden über die `jobs.on()` Funktion 2 Events mit Callbacks gespeichert, damit die Library weiß was sie Ausführen soll falls der Job fehlerfrei abgearbeitet wurde oder ein Fehler eingetreten ist.

Als nächstes rufen wir die Funktion `jobs.process()` auf und übergeben das eigentliche Callback zum bearbeiten des Jobs bzw. verschicken des E-Mails.

jobs.process('new email', 20, function (job, done) {
 // email versenden ...
 // email objekt liegt in job.data ...
 // ...

 done && done();
});

Normalerweise wird von Kue nur ein Job auf einmal ausgeführt, das ist bei großen Jobs vielleicht auch sinnvoll, doch beim E-Mails verschicken können wir hier die maximale Anzahl an zu-bearbeitenden Jobs z.B. auf 20 setzen. Das heißt hier werden gleichzeitig 20 Queue Einträge vom Typ E-Mail aus Redis auf einmal rausgenommen und bearbeitet. Node.js ist hier ganz gut darin.

Zu guter letzt müssen wir unsere Funktion nur mehr aufrufen:

newEmailJob({ to: "...", from: "...", ... });

Unter der Haube wird dabei das E-Mail Objekt in Redis gespeichert, zeitgleich hört `jobs.process()` aber auch auf Änderungen in der E-Mail Collection und führt daraufhin für jeden neuen Eintrag die selbst-definierte Callback Funktion aus (nämlich das E-Mail versenden). Sobald dies geschehen ist wird der Eintrag auch wieder aus Redis rausgelöscht. Wer genaueres wissen möchte und seine Queue beispielsweise in einer anderen Sprache implementieren möchte kann sich hier wieder passende Libraries suchen oder sich direkt die PUB/SUB Methoden für Redis ansehen.

Summary

Queues können für allerlei Abarbeitungen verwendet werden um Requests im Web nicht zu blockieren. Redis ist genau hier ein sehr gutes Tool dafür. Läuft man beispielsweise auch noch auf PHP und hat ein blockierendes I/O System sind Queues umso wichtiger.

1 Kommentar

  • Christoph Fabritz am 13.05.2014 um 01:24

    “Das heißt hier werden gleichzeitig 20 Queue Einträge vom Typ E-Mail aus Redis auf einmal rausgenommen und bearbeitet. Node.js ist hier ganz gut darin.”

    Node.js und Redis sind single-threaded. Gleichzeitig geschieht bei denen immer nur eine Sache.

The comments are closed.