Si vous avez choisi PaaS comme hébergement pour votre application, vous avez probablement eu ou aurez ce problème: Votre application est déployée dans de petits "conteneurs" (appelés "conteneurs"). dynos à Heroku, ou engrenages dans OpenShift) et vous souhaitez le redimensionner.
Pour ce faire, vous augmentez le nombre de conteneurs et chaque instance de votre application s'exécute à peu près sur une autre machine virtuelle. Cela est bon pour un certain nombre de raisons, mais cela signifie également que les instances ne partagent pas de mémoire..
Dans ce tutoriel, je vais vous montrer comment surmonter ce petit inconvénient..
Lorsque vous avez choisi l'hébergement PaaS, je suppose que vous aviez à l'esprit la mise à l'échelle. Peut-être que votre site a déjà été témoin de l'effet Slashdot ou que vous souhaitez vous y préparer. De toute façon, faire en sorte que les instances communiquent les unes avec les autres est assez simple.
N'oubliez pas que dans l'article, je suppose que vous avez déjà une application Node.js écrite et en cours d'exécution..
Tout d'abord, vous devez préparer votre base de données Redis. J'aime utiliser Redis To Go, car la configuration est très rapide et si vous utilisez Heroku, il y a un add-on (bien que votre compte doive avoir une carte de crédit attribuée). Il existe également Redis Cloud, qui inclut davantage de stockage et de sauvegardes.
À partir de là, la configuration de Heroku est assez simple: sélectionnez le module complémentaire sur la page des modules complémentaires Heroku, puis sélectionnez Redis Cloud ou Redis To Go, ou utilisez l’une des commandes suivantes (notez que la première est destinée à Redis To Go). et le second est pour Redis Cloud):
addons $ heroku: ajoutez redistogo addons $ heroku: ajoutez rediscloud
À ce stade, nous devons ajouter le module de nœud requis à la package.json
fichier. Nous allons utiliser le module recommandé node_redis. Ajouter cette ligne à votre package.json
fichier, dans la section des dépendances:
"node_redis": "0.11.x"
Si vous le souhaitez, vous pouvez également inclure embauché
, une bibliothèque performante écrite en C, qui node_redis
utilisera s'il est disponible:
"embauché": "0.1.x"
Selon la manière dont vous avez créé votre base de données Redis et le fournisseur PaaS que vous utilisez, la configuration de la connexion sera légèrement différente. Vous avez besoin hôte
, Port
, Nom d'utilisateur
, et mot de passe
pour votre connexion.
Heroku stocke tout dans les variables de configuration sous forme d'URL. Vous devez extraire les informations dont vous avez besoin en utilisant Node's url
module (config var pour Redis To Go est process.env.REDISTOGO_URL
et pour Redis Cloud process.env.REDISCLOUD_URL
). Ce code se trouve en haut de votre fichier d'application principal:
var redis = require ('redis'); var url = require ('url'); var redisURL = url.parse (YOUR_CONFIG_VAR_HERE); var client = redis.createClient (redisURL.host, redisURL.port); client.auth (redisURL.auth.split (':') [1]);
Si vous avez créé la base de données manuellement ou utilisez un fournisseur autre que Heroku, vous devez déjà disposer des options de connexion et des informations d'identification. Utilisez-les simplement:
var redis = require ('redis'); var client = redis.createClient (YOUR_HOST, YOUR_PORT); client.auth (YOUR_PASSWORD);
Après cela, nous pouvons commencer à travailler sur la communication entre les instances.
L'exemple le plus simple consiste simplement à envoyer des informations à d'autres instances que vous venez de démarrer. Par exemple, vous pouvez afficher ces informations dans le panneau d'administration..
Avant de faire quoi que ce soit, créez une autre connexion nommée client2
. Je vais expliquer pourquoi nous en avons besoin plus tard.
Commençons par envoyer simplement le message que nous avons commencé. C'est fait en utilisant le publier()
méthode du client. Il faut deux arguments: le canal auquel on veut envoyer le message et le texte du message:
client.publish ('instances', 'start');
C'est tout ce dont vous avez besoin pour envoyer le message. Nous pouvons écouter les messages dans le message
gestionnaire d'événement (notez que nous l'appelons sur notre deuxième client):
client2.on ('message', fonction (canal, message)
Le rappel est passé les mêmes arguments que nous passons à la publier()
méthode. Maintenant, affichons ces informations dans la console:
if ((channel == 'instances') et (message == 'start')) console.log ('Nouvelle instance démarrée!'); );
La dernière chose à faire est de vous abonner au canal que nous utiliserons:
client2.subscribe ('instances');
Nous avons utilisé deux clients pour cela parce que lorsque vous appelez souscrire()
sur le client, sa connexion est commutée sur le abonné mode. À partir de ce moment, les seules méthodes que vous pouvez appeler sur le serveur Redis sont SOUSCRIRE
et SE DÉSABONNER
. Donc si nous sommes dans le abonné mode nous pouvons publier()
messages.
Si vous le souhaitez, vous pouvez également envoyer un message lorsque l'instance est en cours de fermeture. Vous pouvez également écouter le message. SIGTERM
événement et envoyez le message au même canal:
process.on ('SIGTERM', function () client.publish ('instances', 'stop'); process.exit (););
Pour gérer ce cas dans le message
gestionnaire ajouter ceci sinon si
dedans là:
else if ((channel == 'instances') et (message == 'stop')) console.log ('Instance stoppée!');
Donc, ça ressemble à ça après:
client2.on ('message', fonction (canal, message) si ((canal == 'instances') et (message == 'démarrer')) console.log ('Nouvelle instance démarrée!'); sinon si ( (channel == 'instances') et (message == 'stop')) console.log ('Instance stoppée!'););
Notez que si vous testez sous Windows, il ne prend pas en charge la SIGTERM
signal.
Pour le tester localement, démarrez votre application plusieurs fois et observez ce qui se passe dans la console. Si vous souhaitez tester le message de fin, n'émettez pas le Ctrl + C
commande dans le terminal, utilisez plutôt tuer
commander. Notez que ceci n'est pas supporté sous Windows, vous ne pouvez donc pas le vérifier..
Tout d'abord, utilisez le ps
commande pour vérifier quel identifiant votre processus a-pipe à grep
pour le rendre plus facile:
$ ps -aux | grep your_apps_name
La deuxième colonne de la sortie est l'ID que vous recherchez. N'oubliez pas qu'il y aura également une ligne pour la commande que vous venez d'exécuter. Maintenant, exécutez le tuer
commande en utilisant 15
pour le signal c'est SIGTERM
:
$ kill -15 PID
PID
est votre identifiant de processus.
Maintenant que vous savez utiliser le protocole Redis Pub / Sub, vous pouvez aller au-delà du simple exemple présenté précédemment. Voici quelques cas d'utilisation qui peuvent être utiles.
Celui-ci est extrêmement utile si vous utilisez Express.js comme infrastructure. Si votre application prend en charge les connexions utilisateur, ou pratiquement tout ce qui utilise des sessions, vous voudrez vous assurer que les sessions utilisateur sont préservées, que l'instance redémarre ou non, que l'utilisateur se déplace vers un emplacement géré par un autre utilisateur ou que l'utilisateur est commuté à un autre cas parce que l'original est descendu.
Quelques points à retenir:
Nous aurons besoin du module connect-redis. La version dépend de la version d'Express que vous utilisez. Celui-ci est pour Express 3.x:
"connect-redis": "1.4.7"
Et ceci pour Express 4.x:
"connect-redis": "2.x"
Maintenant, créez une autre connexion Redis nommée client_sessions
. L’utilisation du module dépend à nouveau de la version Express. Pour 3.x vous créez le RedisStore
comme ça:
var RedisStore = require ('connect-redis') (express)
Et en 4.x vous devez passer le session express
comme paramètre:
var session = require ('express-session'); var RedisStore = require ('connect-redis') (session);
Après cela, la configuration est la même dans les deux versions:
app.use (session (store: new RedisStore (client: client_sessions), secret_session), secret: 'votre chaîne secrète'));
Comme vous pouvez le constater, nous passons notre client Redis en tant que client
propriété de l'objet transmis à RedisStore
Le constructeur de, puis nous passons le magasin à la session
constructeur.
Désormais, si vous démarrez votre application, vous vous connectez ou initiez une session et redémarrez l'instance, votre session sera préservée. La même chose se produit lorsque l'instance est commutée pour l'utilisateur.
Disons que vous avez une instance complètement séparée (dyno travailleur sur Heroku) pour effectuer davantage de travaux qui consomment des ressources, tels que des calculs complexes, le traitement de données dans la base de données ou l’échange de nombreuses données avec un service externe. Vous voudrez que les instances "normales" (et donc les utilisateurs) connaissent le résultat de ce travail une fois terminé.
Selon que vous souhaitez que les instances Web envoient des données à l’utilisateur, vous aurez besoin d’une ou deux connexions (appelons-les client_sub
et client_pub
sur le travailleur aussi). Vous pouvez également réutiliser toute connexion qui n’abonne rien (comme celle que vous utilisez pour les sessions Express) au lieu de la client_pub
.
Désormais, lorsque l'utilisateur souhaite exécuter l'action, vous publiez le message sur le canal réservé à cet utilisateur et à ce travail spécifique:
// cela entre dans votre gestionnaire de requêtes client_pub.publish ('JOB: USERID: JOBNAME: START', JSON.stringify (THEDATAYOUWANTTOSEND)); client_sub.subscribe ('JOB: USERID: JOBNAME: PROGRESS');
Bien sûr, vous devrez remplacer IDENTIFIANT D'UTILISATEUR
et NOM DU TRAVAIL
avec des valeurs appropriées. Vous devriez aussi avoir le message
gestionnaire préparé pour le client_sub
lien:
client_sub.on ('message', fonction (canal, message) var USERID = channel.split (':') [1]; if (message == 'DONE') client_sub.unsubscribe (canal); sockets [USERID] .emit (channel, message););
Ceci extrait le IDENTIFIANT D'UTILISATEUR
à partir du nom du canal (assurez-vous de ne pas vous abonner à des canaux non liés aux travaux des utilisateurs sur cette connexion) et envoyez le message au client approprié. En fonction de la bibliothèque WebSocket que vous utilisez, il sera possible d'accéder à un socket par son ID..
Vous vous demandez peut-être comment l'instance de travail peut s'abonner à tous ces canaux. Bien sûr, vous ne voulez pas juste faire quelques boucles sur tous les possibles IDENTIFIANT D'UTILISATEUR
le sable NOM DU TRAVAIL
s. le psubscribe ()
La méthode accepte un motif comme argument pour pouvoir souscrire à tous EMPLOI:*
canaux:
// ce code va à l'instance de travailleur // et vous l'appelez UNE FOIS client_sub.psubscribe ('JOB: *')
Il existe quelques problèmes que vous pouvez rencontrer lors de l’utilisation de Pub / Sub:
message
gestionnaire avant d'appeler souscrire()
, et que vous appelez souscrire()
sur une instance avant d'appeler publier()
de l'autre.