Non classé

Utilisation de WebSockets et d'équilibreurs de charge Deuxième partie: idk.dev – Bien installer son serveur

Par Titanfall , le 8 juillet 2020 - 7 minutes de lecture

[bzkshopping keyword= »Minecraft » count= »8″ template= »grid »]

Cet article a été écrit par Robert Zhu, développeur principal avocat à AWS.

Cet article fait suite à un blog que j'ai publié plus tôt sur l'utilisation des équilibreurs de charge sur Amazon Lightsail. Dans cet article, je présente quelques défis et solutions courants lors de la combinaison d'applications avec état et d'équilibreurs de charge. Je commence par une simple application WebSocket dans Amazon Lightsail qui compte le nombre de secondes de connexion du client. Ensuite, j'ajoute un équilibreur de charge Lightsail et je vous montre comment l'application effectue le routage et les tentatives. Commençons.

WebSockets

Les WebSockets sont des prises bilatérales persistantes qui permettent une communication bidirectionnelle entre un client et un serveur. Les programmes utilisent souvent WebSockets pour fournir des fonctionnalités en temps réel telles que le chat et les jeux. Commençons par un exemple de code pour un simple serveur WebSocket:

const WebSocket = require ("ws");
nom const = demande ("./ randomName");
serveur const = nécessite ("http"). createServer ();
const express = require ("express");
const app = express ();

console.log (`Ce serveur est nommé: $ name`);

// fichiers serveur du répertoire public
server.on ("demande", app.use (express.static ("public")));

// demande au serveur WebSocket d'utiliser le même serveur HTTP
const wss = new WebSocket.Server (
  serveur,
);

wss.on ("connexion", connexion de fonction (ws, req) 
  const clientId = req.url.replace ("/? id =", "");
  console.log (`Client lié à l'ID: $ clientId`);

  la n = 0;
  const interval = setInterval (() => 
    ws.send (`$ name: vous êtes connecté depuis $ n ++ secondes`);
  , 1000);

  ws.on ("close", () => 
    clearInterval (interval);
  );
);

const port = process.env.PORT || 80;
server.list (port, () => 
  console.log (`Serveur écoutant sur le port $ port`);
);

Nous servons des fichiers statiques de Publique répertoire et demandes de connexion WebSocket sur le même port. Une requête HTTP entrante provenant du navigateur se charge public / index.html, et une connexion WebSocket lancée à partir des déclencheurs client wss.on ("connexion", …) code. Après avoir reçu une connexion WebSocket, j'ai mis en place un rappel récurrent où je dis au client combien de temps il a été connecté. Regardons le code client:

buttonConnect.onclick = async () => 
  const serverAddress = inputServerAddress.value;
  messages.innerHTML = "";
  instructions.parentElement.removeChild (instructions);

  appendMessage (`Connexion à $ serverAddress`);

  essayez 
    laissez réessayer = 0;
    pendant (réessayer) < 50) 
      appendMessage(`establishing connection... retry #$retries`);
      await runSession(serverAddress);
      await sleep(1500);
      retries++;
    

    appendMessage("Reached maximum retries, giving up.");
   catch (e) 
    appendMessage(e.message 
;

async function runSession(address) 
  const ws = new WebSocket(address);

  ws.addEventListener("open", () => 
    appendMessage ("connecté au serveur");
  );

  ws.addEventListener ("message", (data) => 
    console.log (données);
    appendMessage (données);
  );

  retourner une nouvelle promesse ((résolue) => 
    ws.addEventListener ("close", () => 
      appendMessage ("Connexion perdue avec le serveur.");
      Résoudre();
    );
  );

J'utilise l'API DOM WebSocket pour me connecter au serveur. Une fois connecté, j'ajoute tous les messages reçus à la console et à l'écran via la personnalisation appendMessage fonction. Si le client perd la connexion, il essaiera de se reconnecter jusqu'à 50 fois. Lançons-le:

Supposons maintenant que j'exécute une application très exigeante en temps réel et que je doive faire évoluer la capacité du serveur au-delà d'un seul hôte. Comment dois-je procéder? Je crée deux instances Ubuntu 18.04. Lorsque les instances sont en place, j'envoie à chacune et exécute les commandes suivantes:

mise à jour sudo apt-get
sudo apt-get -y install nodejs npm
git clone https://github.com/robzhu/ws-time
installation cd ws-time && npm
nœud server.js

Sélectionnez lors de l'installation Oui lorsque l'invite s'affiche:

Sélectionner

Gardez ces sessions SSH ouvertes, vous en aurez besoin sous peu. Créez ensuite l'équilibreur de charge dans Amazon Lightsail et attachez les instances:

capture d'écran d'instances cibles pour les équilibreurs de charge

Remarque: L'équilibre de charge Lightsail ne fonctionne que pour le port 80, ce qui explique en partie pourquoi j'utilise le même port pour les requêtes HTTP et WebSocket.

Copiez le nom DNS de l'équilibre de charge, ouvrez-le dans un nouvel onglet de navigateur et collez-le dans l'adresse du serveur WebSocket du format:

ws: //

capture d'écran de ce à quoi devrait ressembler l'adresse du serveur

Assurez-vous que l'adresse du serveur ne commence pas accidentellement par «ws: // http: //…»

Ensuite, vous trouverez la session SSH qui a accepté la connexion. Il ressemble à ça:

section ssh acceptée

Le serveur enregistre l'ID client lorsqu'il reçoit une connexion.

Tuer ce processus désactivera le client et exécutera la logique de l'essai, ce qui, espérons-le, entraînera l'équilibre de charge pour router le client vers un nœud sain. Appuyez ensuite sur Déconnecter le client. Arrêtez le processus sur le serveur après quelques secondes et vous devriez voir le client se reconnecter à une instance saine:

Que rechercher lors de la connexion à une instance saine

Le procès du client a été mené à un événement sain dès la première tentative. Cela est dû à l'algorithme round robin utilisé par l'équilibre de charge Lightsail. En production, vous ne devez pas vous attendre à ce que l'équilibre de charge détecte immédiatement un nœud malsain. Si l'équilibre de charge continue d'acheminer les connexions entrantes vers un nœud malsain, le client devra réessayer plusieurs tentatives avant de se reconnecter. S'il s'agit d'un système à grande échelle, nous aimerions implémenter un recul exponentiel aux intervalles d'essai pour éviter d'écraser les autres nœuds du cluster (également connu sous le nom d'équipage de troupeau tonitruant).

Notez que le message «vous avez été connecté pendant X secondes» réinitialise X à 0 après la reconnexion du client. Que faire si vous souhaitez rendre le basculement transparent pour l'utilisateur? Le problème est que la durée de connexion (X) est stockée dans le processus NodeJS que nous avons tué. Cet état est perdu si le processus meurt ou si l'hôte tombe en panne. La solution n'est pas surprenante: déplacez l'état du serveur WebSocket dans un cache distribué, tel que redis.

Contrôles de santé approfondis

Lorsque vous avez attaché vos instances à l'équilibre de charge, vos vérifications de l'état ont réussi car les équilibrages de charge Lightsail émettent une demande HTTP pour le chemin par défaut (où vous servez index.html). Cependant, si vous vous attendez à ce que la majeure partie de la charge de votre serveur provienne d'E / S sur des connexions WebSocket, la possibilité de servir notre index.html le dossier n'est pas un bon bilan de santé. Vous pouvez implémenter un meilleur bilan de santé comme celui-ci:

app.get (& # 39; / bilan de santé & # 39;, (req, res) => 
  const serverHasCapacity = getAverageNetworkUsage () <0,6;
  if (serverHasCapacity) res.status (200) .send ("ok");
  res.status (400) .send ("le serveur est surchargé");
);

Cela a amené l'équilibre de charge à considérer un nœud comme "malsain" lorsque l'utilisation du réseau du nœud cible atteint un seuil. En réponse, l'équilibre de charge arrête le routage des nouvelles connexions entrantes vers ce nœud. Notez cependant que l'équilibre de charge ne mettra pas fin aux connexions existantes avec un nœud surabonné.

Lorsque vous travaillez avec des connexions persistantes ou des sessions persistantes, laissez toujours un tampon de capacité. Par exemple, ne marquez pas le serveur comme défectueux uniquement lorsqu'il atteint une capacité de 100%. En effet, les connexions existantes ou les clients persistants continueront de générer du trafic pour ce nœud, et certaines charges de travail peuvent augmenter l'utilisation du serveur au-delà de son seuil (comme une salle de conversation qui devient soudainement très occupée).

Conclusion

J'espère que ce billet vous a donné une idée claire de la façon d'utiliser les équilibreurs de charge pour améliorer l'évolutivité des applications avec état et comment implémenter une telle solution à l'aide d'instances Amazon Lightsail et d'équilibreurs de charge. N'hésitez pas à commenter et à essayer cette solution vous-même.

Click to rate this post!
[Total: 0 Average: 0]

Commentaires

Laisser un commentaire

Votre commentaire sera révisé par les administrateurs si besoin.