Créer une application de diffusion en continu de code en temps réel avec Socket.IO et l’héberger sur Azure
La création d’une expérience en temps réel comme la fonctionnalité de cocréation dans Microsoft Word peut être difficile.
Grâce à ses API faciles à utiliser, Socket.IO s’est avéré être une bibliothèque pour la communication en temps réel entre les clients et un serveur. Toutefois, Socket.IO utilisateurs signalent souvent des difficultés concernant la mise à l’échelle des connexions socket.IO. Avec Web PubSub pour Socket.IO, les développeurs n’ont plus besoin de s’inquiéter de la gestion des connexions persistantes.
Vue d’ensemble
Cet article explique comment créer une application qui permet à un coder de diffuser en continu des activités de codage vers un public. Vous générez cette application à l’aide de :
- Éditeur Monaco, éditeur de code qui alimente Visual Studio Code.
- Express, infrastructure web Node.js.
- API que la bibliothèque Socket.IO fournit pour la communication en temps réel.
- Héberger des connexions Socket.IO qui utilisent Web PubSub pour Socket.IO.
L’application terminée
L’application terminée permet à l’utilisateur d’un éditeur de code de partager un lien web via lequel les utilisateurs peuvent regarder la saisie.
Pour garder les procédures prioritaires et digestibles en environ 15 minutes, cet article définit deux rôles d’utilisateur et ce qu’ils peuvent faire dans l’éditeur :
- Un enregistreur, qui peut taper dans l’éditeur en ligne et le contenu est diffusé en continu
- Visionneuses, qui reçoivent du contenu en temps réel tapé par l’enregistreur et ne peuvent pas modifier le contenu
Architecture
Article | Objectif | Avantages |
---|---|---|
bibliothèque Socket.IO | Fournit un mécanisme d’échange de données bidirectionnel à faible latence entre l’application principale et les clients | API faciles à utiliser qui couvrent la plupart des scénarios de communication en temps réel |
Web PubSub pour Socket.IO | Héberge WebSocket ou des connexions persistantes basées sur des sondages avec des clients Socket.IO | Prise en charge de 100 000 connexions simultanées ; architecture d’application simplifiée |
Prérequis
Pour suivre toutes les étapes décrites dans cet article, vous avez besoin des éléments suivants :
- Un compte Azure. Si vous n’en avez pas, créez un compte gratuit Azure avant de commencer.
- Azure CLI (version 2.29.0 ou ultérieure) ou Azure Cloud Shell pour gérer les ressources Azure.
- Connaissance de base des API de Socket.IO.
Créer une ressource Web PubSub pour Socket.IO
Utilisez Azure CLI pour créer la ressource :
az webpubsub create -n <resource-name> \
-l <resource-location> \
-g <resource-group> \
--kind SocketIO \
--sku Free_F1
Obtenir une chaîne de connexion
Un chaîne de connexion vous permet de vous connecter à Web PubSub pour Socket.IO.
Exécutez les commandes suivantes : Conservez le chaîne de connexion retourné quelque part, car vous en aurez besoin lorsque vous exécutez l’application plus loin dans cet article.
az webpubsub key show -n <resource-name> \
-g <resource-group> \
--query primaryKey \
-o tsv
Écrire le code côté serveur de l’application
Commencez à écrire le code de votre application en travaillant côté serveur.
Générer un serveur HTTP
Créez un projet Node.js :
mkdir codestream cd codestream npm init
Installez le Kit de développement logiciel (SDK) serveur et Express :
npm install @azure/web-pubsub-socket.io npm install express
Importez les packages requis et créez un serveur HTTP pour traiter des fichiers statiques :
/*server.js*/ // Import required packages const express = require('express'); const path = require('path'); // Create an HTTP server based on Express const app = express(); const server = require('http').createServer(app); app.use(express.static(path.join(__dirname, 'public')));
Définissez un point de terminaison appelé
/negotiate
. Un client writer atteint d’abord ce point de terminaison. Ce point de terminaison retourne une réponse HTTP. La réponse contient un point de terminaison que le client doit utiliser pour établir une connexion persistante. Elle retourne également uneroom
valeur à laquelle le client est affecté./*server.js*/ app.get('/negotiate', async (req, res) => { res.json({ url: endpoint room_id: Math.random().toString(36).slice(2, 7), }); }); // Make the Socket.IO server listen on port 3000 io.httpServer.listen(3000, () => { console.log('Visit http://localhost:%d', 3000); });
Créer le serveur Web PubSub pour Socket.IO
Importez web PubSub pour Socket.IO SDK et définissez les options suivantes :
/*server.js*/ const { useAzureSocketIO } = require("@azure/web-pubsub-socket.io"); const wpsOptions = { hub: "codestream", connectionString: process.argv[2] }
Créez un serveur Web PubSub pour Socket.IO serveur :
/*server.js*/ const io = require("socket.io")(); useAzureSocketIO(io, wpsOptions);
Les deux étapes sont légèrement différentes de la façon dont vous créez normalement un serveur Socket.IO, comme décrit dans cette documentation Socket.IO. Avec ces deux étapes, votre code côté serveur peut décharger la gestion des connexions persistantes vers un service Azure. Avec l’aide d’un service Azure, votre serveur d’applications agit uniquement comme un serveur HTTP léger.
Implémenter une logique métier
Maintenant que vous avez créé un serveur Socket.IO hébergé par Web PubSub, vous pouvez définir la façon dont les clients et le serveur communiquent à l’aide des API de Socket.IO. Ce processus est appelé implémentation de la logique métier.
Une fois qu’un client est connecté, le serveur d’applications indique au client qu’il est connecté en envoyant un événement personnalisé nommé
login
./*server.js*/ io.on('connection', socket => { socket.emit("login"); });
Chaque client émet deux événements auxquels le serveur peut répondre :
joinRoom
etsendToRoom
. Une fois que le serveur obtient laroom_id
valeur qu’un client souhaite joindre, vous utilisezsocket.join
l’API socket.IO pour joindre le client cible à la salle spécifiée./*server.js*/ socket.on('joinRoom', async (message) => { const room_id = message["room_id"]; await socket.join(room_id); });
Une fois qu’un client est joint, le serveur informe le client du résultat réussi en envoyant un
message
événement. Lorsque le client reçoit unmessage
événement avec un type deackJoinRoom
, le client peut demander au serveur d’envoyer l’état de l’éditeur le plus récent./*server.js*/ socket.on('joinRoom', async (message) => { // ... socket.emit("message", { type: "ackJoinRoom", success: true }) });
/*client.js*/ socket.on("message", (message) => { let data = message; if (data.type === 'ackJoinRoom' && data.success) { sendToRoom(socket, `${room_id}-control`, { data: 'sync'}); } // ... });
Lorsqu’un client envoie un
sendToRoom
événement au serveur, le serveur diffuse les modifications apportées à l’état de l’éditeur de code dans la salle spécifiée. Tous les clients de la salle peuvent désormais recevoir la dernière mise à jour.socket.on('sendToRoom', (message) => { const room_id = message["room_id"] const data = message["data"] socket.broadcast.to(room_id).emit("message", { type: "editorMessage", data: data }); });
Écrire le code côté client de l’application
Maintenant que les procédures côté serveur sont terminées, vous pouvez travailler côté client.
Configuration initiale
Vous devez créer un client Socket.IO pour communiquer avec le serveur. La question est de savoir avec quel serveur le client doit établir une connexion persistante. Étant donné que vous utilisez Web PubSub pour Socket.IO, le serveur est un service Azure. Rappelez-vous que vous avez défini un itinéraire /negotiate pour servir les clients à Un point de terminaison web PubSub pour Socket.IO.
/*client.js*/
async function initialize(url) {
let data = await fetch(url).json()
updateStreamId(data.room_id);
let editor = createEditor(...); // Create an editor component
var socket = io(data.url, {
path: "/clients/socketio/hubs/codestream",
});
return [socket, editor, data.room_id];
}
La initialize(url)
fonction organise quelques opérations d’installation ensemble :
- Récupère le point de terminaison vers un service Azure à partir de votre serveur HTTP
- Crée une instance de l’éditeur Monaco
- Établit une connexion persistante avec Web PubSub pour Socket.IO
Client Writer
Comme mentionné précédemment, vous avez deux rôles d’utilisateur côté client : writer et viewer. Tout ce que les types d’enregistreurs sont diffusés à l’écran de la visionneuse.
Obtenez le point de terminaison sur Web PubSub pour Socket.IO et la
room_id
valeur :/*client.js*/ let [socket, editor, room_id] = await initialize('/negotiate');
Lorsque le client writer est connecté au serveur, le serveur envoie un
login
événement à l’enregistreur. L’enregistreur peut répondre en demandant au serveur de se joindre à une salle spécifiée. Toutes les 200 millisecondes, le client writer envoie l’état de l’éditeur le plus récent à la salle. Une fonction nomméeflush
organise la logique d’envoi./*client.js*/ socket.on("login", () => { updateStatus('Connected'); joinRoom(socket, `${room_id}`); setInterval(() => flush(), 200); // Update editor content // ... });
Si un enregistreur n’apporte aucune modification,
flush()
ne fait rien et retourne simplement. Sinon, les modifications apportées à l’état de l’éditeur sont envoyées à la salle./*client.js*/ function flush() { // No changes from editor need to be flushed if (changes.length === 0) return; // Broadcast the changes made to editor content sendToRoom(socket, room_id, { type: 'delta', changes: changes version: version++, }); changes = []; content = editor.getValue(); }
Lorsqu’un nouveau client de visionneuse est connecté, la visionneuse doit obtenir l’état complet le plus récent de l’éditeur. Pour ce faire, un message qui contient des
sync
données est envoyé au client writer. Le message demande au client writer d’envoyer l’état complet de l’éditeur./*client.js*/ socket.on("message", (message) => { let data = message.data; if (data.data === 'sync') { // Broadcast the full content of the editor to the room sendToRoom(socket, room_id, { type: 'full', content: content version: version, }); } });
Client visionneuse
Comme le client writer, le client de visionneuse crée son client Socket.IO via
initialize()
. Lorsque le client de la visionneuse est connecté et reçoit unlogin
événement du serveur, il demande au serveur de se joindre à la salle spécifiée. La requêteroom_id
spécifie la salle./*client.js*/ let [socket, editor] = await initialize(`/register?room_id=${room_id}`) socket.on("login", () => { updateStatus('Connected'); joinRoom(socket, `${room_id}`); });
Lorsqu’un client de visionneuse reçoit un
message
événement du serveur et que le type de données estackJoinRoom
, le client de la visionneuse demande au client writer dans la salle d’envoyer l’état complet de l’éditeur./*client.js*/ socket.on("message", (message) => { let data = message; // Ensures the viewer client is connected if (data.type === 'ackJoinRoom' && data.success) { sendToRoom(socket, `${id}`, { data: 'sync'}); } else //... });
Si le type de données est
editorMessage
, le client de visionneuse met à jour l’éditeur en fonction de son contenu réel./*client.js*/ socket.on("message", (message) => { ... else if (data.type === 'editorMessage') { switch (data.data.type) { case 'delta': // ... Let editor component update its status break; case 'full': // ... Let editor component update its status break; } } });
Implémentez et
sendToRoom()
utilisezjoinRoom()
les API de Socket.IO :/*client.js*/ function joinRoom(socket, room_id) { socket.emit("joinRoom", { room_id: room_id, }); } function sendToRoom(socket, room_id, data) { socket.emit("sendToRoom", { room_id: room_id, data: data }); }
Exécution de l'application
Localiser le dépôt
Les sections précédentes ont abordé la logique principale liée à la synchronisation de l’état de l’éditeur entre les visionneuses et l’enregistreur. Vous trouverez le code complet dans le référentiel d’exemples.
Cloner le référentiel
Vous pouvez cloner le référentiel et l’exécuter npm install
pour installer les dépendances du projet.
Démarrer le serveur
node server.js <web-pubsub-connection-string>
Il s’agit de la chaîne de connexion que vous avez reçue à une étape antérieure.
Lire avec l’éditeur de code en temps réel
Ouvrez un onglet de navigateur. Ouvrez http://localhost:3000
un autre onglet avec l’URL affichée sur la première page web.
Si vous écrivez du code sous le premier onglet, vous devez voir votre saisie reflétée en temps réel sur l’autre onglet. Web PubSub pour Socket.IO facilite la transmission de messages dans le cloud. Votre express
serveur sert uniquement le fichier statique index.html
et le /negotiate
point de terminaison.