Skapa en kodströmningsapp i realtid med hjälp av Socket.IO och vara värd för den i Azure
Det kan vara svårt att skapa en realtidsupplevelse som funktionen för samskapande i Microsoft Word .
Genom sina lätthanterade API:er har Socket.IO visat sig vara ett bibliotek för kommunikation i realtid mellan klienter och en server. Men Socket.IO användare rapporterar ofta problem med att skala Socket.IO-anslutningar. Med Web PubSub för Socket.IO behöver utvecklare inte längre oroa sig för att hantera beständiga anslutningar.
Viktigt!
Råa anslutningssträng visas endast i den här artikeln i demonstrationssyfte.
En anslutningssträng innehåller den auktoriseringsinformation som krävs för att ditt program ska få åtkomst till Azure Web PubSub-tjänsten. Åtkomstnyckeln i anslutningssträng liknar ett rotlösenord för din tjänst. Skydda alltid dina åtkomstnycklar i produktionsmiljöer. Använd Azure Key Vault för att hantera och rotera dina nycklar på ett säkert sätt och skydda anslutningen med WebPubSubServiceClient
.
Undvik att distribuera åtkomstnycklar till andra användare, hårdkoda dem eller spara dem var som helst i oformaterad text som är tillgänglig för andra. Rotera dina nycklar om du tror att de har komprometterats.
Översikt
Den här artikeln visar hur du skapar en app som gör att en kodare kan strömma kodningsaktiviteter till en målgrupp. Du skapar det här programmet med hjälp av:
- Monaco Editor, kodredigeraren som driver Visual Studio Code.
- Express, ett Node.js webbramverk.
- API:er som Socket.IO-biblioteket tillhandahåller för kommunikation i realtid.
- Värd Socket.IO anslutningar som använder Web PubSub för Socket.IO.
Den färdiga appen
Med den färdiga appen kan användaren av en kodredigerare dela en webblänk genom vilken personer kan titta på skrivningen.
För att hålla procedurerna fokuserade och lättsmälta på cirka 15 minuter definierar den här artikeln två användarroller och vad de kan göra i redigeraren:
- En författare som kan skriva in onlineredigeraren och innehållet strömmas
- Tittare som får innehåll i realtid som skrivs av författaren och inte kan redigera innehållet
Arkitektur
Artikel | Syfte | Förmåner |
---|---|---|
Socket.IO bibliotek | Tillhandahåller en mekanism för dubbelriktat datautbyte med låg svarstid mellan serverdelsprogrammet och klienterna | Lätt att använda API:er som täcker de flesta kommunikationsscenarier i realtid |
Web PubSub för Socket.IO | Är värd för WebSocket- eller avsökningsbaserade beständiga anslutningar med Socket.IO klienter | Stöd för 100 000 samtidiga anslutningar; förenklad programarkitektur |
Förutsättningar
Om du vill följa alla steg i den här artikeln behöver du:
- Ett Azure-konto. Om du inte har en Azure-prenumeration skapar du ett kostnadsfritt Azure-konto innan du börjar.
- Azure CLI (version 2.29.0 eller senare) eller Azure Cloud Shell för att hantera Azure-resurser.
- Grundläggande kunskaper om Socket.IO:s API:er.
Skapa en Web PubSub för Socket.IO resurs
Använd Azure CLI för att skapa resursen:
az webpubsub create -n <resource-name> \
-l <resource-location> \
-g <resource-group> \
--kind SocketIO \
--sku Free_F1
Skaffa en anslutningssträng
Med en anslutningssträng kan du ansluta till Web PubSub för Socket.IO.
Kör följande kommandon. Behåll de returnerade anslutningssträng någonstans, eftersom du behöver det när du kör programmet senare i den här artikeln.
az webpubsub key show -n <resource-name> \
-g <resource-group> \
--query primaryKey \
-o tsv
Skriva programmets kod på serversidan
Börja skriva programmets kod genom att arbeta på serversidan.
Skapa en HTTP-server
Skapa ett Node.js projekt:
mkdir codestream cd codestream npm init
Installera server-SDK och Express:
npm install @azure/web-pubsub-socket.io npm install express
Importera nödvändiga paket och skapa en HTTP-server för att hantera statiska filer:
/*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')));
Definiera en slutpunkt med namnet
/negotiate
. En skrivarklient träffar den här slutpunkten först. Den här slutpunkten returnerar ett HTTP-svar. Svaret innehåller en slutpunkt som klienten ska använda för att upprätta en beständig anslutning. Den returnerar också ettroom
värde som klienten är tilldelad till./*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); });
Skapa Web PubSub för Socket.IO-servern
Importera Web PubSub för Socket.IO SDK och definiera alternativ:
Råa anslutningssträng visas endast i den här artikeln i demonstrationssyfte. Skydda alltid dina åtkomstnycklar i produktionsmiljöer. Använd Azure Key Vault för att hantera och rotera dina nycklar på ett säkert sätt och skydda anslutningen med
WebPubSubServiceClient
./*server.js*/ const { useAzureSocketIO } = require("@azure/web-pubsub-socket.io"); const wpsOptions = { hub: "codestream", connectionString: process.argv[2] }
Skapa en Web PubSub för Socket.IO server:
/*server.js*/ const io = require("socket.io")(); useAzureSocketIO(io, wpsOptions);
De två stegen skiljer sig något från hur du normalt skulle skapa en Socket.IO server, enligt beskrivningen i den här Socket.IO dokumentationen. Med de här två stegen kan koden på serversidan avlasta hanteringen av beständiga anslutningar till en Azure-tjänst. Med hjälp av en Azure-tjänst fungerar programservern endast som en enkel HTTP-server.
Implementera affärslogik
Nu när du har skapat en Socket.IO server som hanteras av Web PubSub kan du definiera hur klienterna och servern kommunicerar med hjälp av Socket.IO:s API:er. Den här processen kallas för implementering av affärslogik.
När en klient har anslutits meddelar programservern klienten att den är inloggad genom att skicka en anpassad händelse med namnet
login
./*server.js*/ io.on('connection', socket => { socket.emit("login"); });
Varje klient genererar två händelser som servern kan svara på:
joinRoom
ochsendToRoom
. När servern har fått detroom_id
värde som en klient vill ansluta till användersocket.join
du från Socket.IO:s API för att ansluta målklienten till det angivna rummet./*server.js*/ socket.on('joinRoom', async (message) => { const room_id = message["room_id"]; await socket.join(room_id); });
När en klient har anslutits informerar servern klienten om det lyckade resultatet genom att skicka en
message
händelse. När klienten tar emot enmessage
händelse med en typ avackJoinRoom
kan klienten be servern att skicka det senaste redigeringstillståndet./*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'}); } // ... });
När en klient skickar en
sendToRoom
händelse till servern sänder servern ändringarna i kodredigerarens tillstånd till det angivna rummet. Alla klienter i rummet kan nu få den senaste uppdateringen.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 }); });
Skriva programmets kod på klientsidan
Nu när procedurerna på serversidan är klara kan du arbeta på klientsidan.
Inledande installation
Du måste skapa en Socket.IO-klient för att kommunicera med servern. Frågan är vilken server klienten ska upprätta en beständig anslutning till. Eftersom du använder Web PubSub för Socket.IO är servern en Azure-tjänst. Kom ihåg att du har definierat en /negotiate-väg för att betjäna klienter en slutpunkt till Web PubSub för 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];
}
Funktionen initialize(url)
organiserar några konfigurationsåtgärder tillsammans:
- Hämtar slutpunkten till en Azure-tjänst från HTTP-servern
- Skapar en Monaco Editor-instans
- Upprättar en beständig anslutning med Web PubSub för Socket.IO
Skrivarklient
Som tidigare nämnts har du två användarroller på klientsidan: författare och visningsprogram. Allt som skrivarna skriver strömmas till visningsprogrammets skärm.
Hämta slutpunkten till Web PubSub för Socket.IO och
room_id
värdet:/*client.js*/ let [socket, editor, room_id] = await initialize('/negotiate');
När skrivarklienten är ansluten till servern skickar servern en
login
händelse till skrivaren. Skrivaren kan svara genom att be servern att ansluta sig till ett angivet rum. Var 200:e millisekunder skickar skrivarklienten det senaste redigeringstillståndet till rummet. En funktion med namnetflush
organiserar den sändande logiken./*client.js*/ socket.on("login", () => { updateStatus('Connected'); joinRoom(socket, `${room_id}`); setInterval(() => flush(), 200); // Update editor content // ... });
Om en skrivare inte gör några ändringar,
flush()
gör ingenting och returnerar helt enkelt. Annars skickas ändringarna i redigeringstillståndet till rummet./*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(); }
När en ny visningsprogramklient är ansluten måste visningsprogrammet få det senaste fullständiga tillståndet för redigeraren. För att uppnå detta skickas ett meddelande som innehåller
sync
data till skrivarklienten. Meddelandet ber skrivarklienten att skicka det fullständiga redigeringstillståndet./*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, }); } });
Viewer-klient
Precis som författarklienten skapar visningsklienten sin Socket.IO-klient via
initialize()
. När visningsprogrammets klient är ansluten och tar emot enlogin
händelse från servern uppmanas servern att ansluta sig till det angivna rummet. Fråganroom_id
anger rummet./*client.js*/ let [socket, editor] = await initialize(`/register?room_id=${room_id}`) socket.on("login", () => { updateStatus('Connected'); joinRoom(socket, `${room_id}`); });
När en visningsklient tar emot en
message
händelse från servern och datatypen ärackJoinRoom
ber visningsprogrammet klienten i rummet att skicka det fullständiga redigeringstillståndet./*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 //... });
Om datatypen är
editorMessage
uppdaterar visningsprogrammet redigeraren enligt dess faktiska innehåll./*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; } } });
Implementera
joinRoom()
ochsendToRoom()
med hjälp av Socket.IO:s API:er:/*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 }); }
Kör appen
Leta upp lagringsplatsen
Föregående avsnitt beskriver kärnlogik som rör synkronisering av redigeringstillståndet mellan visningsprogram och skrivare. Du hittar den fullständiga koden i exempellagringsplatsen.
Klona lagringsplatsen
Du kan klona lagringsplatsen och köra npm install
för att installera projektberoenden.
Starta servern
node server.js <web-pubsub-connection-string>
Det här är den anslutningssträng som du fick i ett tidigare steg.
Spela med kodredigeraren i realtid
Öppna http://localhost:3000
på en webbläsarflik. Öppna en annan flik med url:en som visas på den första webbsidan.
Om du skriver kod på den första fliken bör du se hur du skriver i realtid på den andra fliken. Web PubSub för Socket.IO underlättar meddelandeöverföring i molnet. Servern express
hanterar bara den statiska index.html
filen och /negotiate
slutpunkten.