Een realtime codestreaming-app bouwen met behulp van Socket.IO en deze hosten in Azure
Het bouwen van een realtime ervaring, zoals de functie voor cocreatie in Microsoft Word , kan lastig zijn.
Dankzij de gebruiksvriendelijke API's heeft Socket.IO zich bewezen als een bibliotheek voor realtime communicatie tussen clients en een server. Socket.IO gebruikers melden echter vaak problemen met het schalen van socket.IO-verbindingen. Met Web PubSub voor Socket.IO hoeven ontwikkelaars zich geen zorgen meer te maken over het beheren van permanente verbindingen.
Belangrijk
Onbewerkte verbindingsreeks worden alleen in dit artikel weergegeven voor demonstratiedoeleinden.
Een verbindingsreeks bevat de autorisatiegegevens die nodig zijn voor uw toepassing voor toegang tot de Azure Web PubSub-service. De toegangssleutel in de verbindingsreeks is vergelijkbaar met een hoofdwachtwoord voor uw service. Beveilig uw toegangssleutels altijd in productieomgevingen. Gebruik Azure Key Vault om uw sleutels veilig te beheren en te roteren en uw verbinding te beveiligen.WebPubSubServiceClient
Vermijd het distribueren van toegangssleutels naar andere gebruikers, het coderen ervan of het opslaan van ze ergens in tekst zonder opmaak die toegankelijk is voor anderen. Draai uw sleutels als u denkt dat ze mogelijk zijn aangetast.
Overzicht
In dit artikel wordt beschreven hoe u een app bouwt waarmee een coder codeeractiviteiten naar een doelgroep kan streamen. U bouwt deze toepassing met behulp van:
- Monaco Editor, de code-editor die Visual Studio Code mogelijk maakt.
- Express, een Node.js webframework.
- API's die de Socket.IO-bibliotheek biedt voor realtime communicatie.
- Host Socket.IO verbindingen die gebruikmaken van Web PubSub voor Socket.IO.
De voltooide app
Met de voltooide app kan de gebruiker van een code-editor een webkoppeling delen waarmee mensen het typen kunnen bekijken.
Om de procedures in ongeveer 15 minuten gefocust en begrijpelijk te houden, definieert dit artikel twee gebruikersrollen en wat ze kunnen doen in de editor:
- Een schrijver, die de online editor kan typen en de inhoud wordt gestreamd
- Kijkers die realtime inhoud ontvangen die door de schrijver is getypt en die de inhoud niet kunnen bewerken
Architectuur
Artikel | Doel | Vergoedingen |
---|---|---|
Socket.IO bibliotheek | Biedt een mechanisme voor lage latentie, bidirectionele gegevensuitwisseling tussen de back-endtoepassing en clients | Gebruiksvriendelijke API's die betrekking hebben op de meeste realtime communicatiescenario's |
Web PubSub voor Socket.IO | Hosts WebSocket of op poll gebaseerde permanente verbindingen met Socket.IO-clients | Ondersteuning voor 100.000 gelijktijdige verbindingen; vereenvoudigde toepassingsarchitectuur |
Vereisten
Als u alle stappen in dit artikel wilt volgen, hebt u het volgende nodig:
- Een Azure-account. Als u geen Azure-abonnement hebt, maakt u een gratis Azure-account voordat u begint.
- De Azure CLI (versie 2.29.0 of hoger) of Azure Cloud Shell voor het beheren van Azure-resources.
- Basiskennis van de API's van Socket.IO.
Een Web PubSub maken voor Socket.IO-resource
Gebruik de Azure CLI om de resource te maken:
az webpubsub create -n <resource-name> \
-l <resource-location> \
-g <resource-group> \
--kind SocketIO \
--sku Free_F1
Een verbindingsreeks
Met een verbindingsreeks kunt u verbinding maken met Web PubSub voor Socket.IO.
Voer de volgende opdrachten uit. Bewaar de geretourneerde verbindingsreeks ergens, omdat u deze nodig hebt wanneer u de toepassing verderop in dit artikel uitvoert.
az webpubsub key show -n <resource-name> \
-g <resource-group> \
--query primaryKey \
-o tsv
Code aan de serverzijde van de toepassing schrijven
Begin met het schrijven van de code van uw toepassing door aan de serverzijde te werken.
Een HTTP-server bouwen
Een Node.js-project maken:
mkdir codestream cd codestream npm init
Installeer de server-SDK en Express:
npm install @azure/web-pubsub-socket.io npm install express
Importeer vereiste pakketten en maak een HTTP-server voor statische bestanden:
/*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')));
Definieer een eindpunt met de naam
/negotiate
. Een schrijverclient raakt dit eindpunt eerst. Dit eindpunt retourneert een HTTP-antwoord. Het antwoord bevat een eindpunt dat de client moet gebruiken om een permanente verbinding tot stand te brengen. Het retourneert ook eenroom
waarde waaraan de client is toegewezen./*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); });
De Web PubSub voor Socket.IO-server maken
Importeer de Web PubSub voor Socket.IO SDK en definieer opties:
Onbewerkte verbindingsreeks worden alleen in dit artikel weergegeven voor demonstratiedoeleinden. Beveilig uw toegangssleutels altijd in productieomgevingen. Gebruik Azure Key Vault om uw sleutels veilig te beheren en te roteren en uw verbinding te beveiligen.
WebPubSubServiceClient
/*server.js*/ const { useAzureSocketIO } = require("@azure/web-pubsub-socket.io"); const wpsOptions = { hub: "codestream", connectionString: process.argv[2] }
Maak een Web PubSub voor Socket.IO-server:
/*server.js*/ const io = require("socket.io")(); useAzureSocketIO(io, wpsOptions);
De twee stappen verschillen enigszins van de manier waarop u normaal gesproken een Socket.IO-server zou maken, zoals beschreven in deze Socket.IO documentatie. Met deze twee stappen kan uw code aan de serverzijde permanente verbindingen met een Azure-service offloaden. Met behulp van een Azure-service fungeert uw toepassingsserver alleen als een lichtgewicht HTTP-server.
Bedrijfslogica implementeren
Nu u een Socket.IO-server hebt gemaakt die wordt gehost door Web PubSub, kunt u definiƫren hoe de clients en de server communiceren met behulp van de API's van Socket.IO. Dit proces wordt het implementeren van bedrijfslogica genoemd.
Nadat een client is verbonden, vertelt de toepassingsserver de client dat deze is aangemeld door een aangepaste gebeurtenis met de naam
login
te verzenden./*server.js*/ io.on('connection', socket => { socket.emit("login"); });
Elke client verzendt twee gebeurtenissen waarop de server kan reageren:
joinRoom
ensendToRoom
. Nadat de server deroom_id
waarde heeft opgehaald die een client wil toevoegen, gebruiktsocket.join
u de API van Socket.IO om de doelclient toe te voegen aan de opgegeven ruimte./*server.js*/ socket.on('joinRoom', async (message) => { const room_id = message["room_id"]; await socket.join(room_id); });
Nadat een client is toegevoegd, informeert de server de client over het geslaagde resultaat door een
message
gebeurtenis te verzenden. Wanneer de client eenmessage
gebeurtenis ontvangt met een type,ackJoinRoom
kan de client de server vragen om de meest recente editorstatus te verzenden./*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'}); } // ... });
Wanneer een client een
sendToRoom
gebeurtenis naar de server verzendt, verzendt de server de wijzigingen in de status van de code-editor naar de opgegeven ruimte. Alle clients in de ruimte kunnen nu de nieuwste update ontvangen.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 }); });
Code aan de clientzijde van de toepassing schrijven
Nu de procedures aan de serverzijde zijn voltooid, kunt u aan de clientzijde werken.
Eerste configuratie
U moet een Socket.IO-client maken om met de server te communiceren. De vraag is met welke server de client een permanente verbinding tot stand moet brengen. Omdat u Web PubSub voor Socket.IO gebruikt, is de server een Azure-service. U hebt een /negotiate-route gedefinieerd om clients een eindpunt te leveren aan Web PubSub voor 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];
}
De initialize(url)
functie organiseert een aantal installatiebewerkingen samen:
- Haalt het eindpunt op naar een Azure-service van uw HTTP-server
- Hiermee maakt u een Monaco Editor-exemplaar
- Hiermee wordt een permanente verbinding tot stand gebracht met Web PubSub voor Socket.IO
Writer-client
Zoals eerder vermeld, hebt u twee gebruikersrollen aan de clientzijde: writer en viewer. Alles wat de schrijftypen zijn, wordt naar het scherm van de kijker gestreamd.
Haal het eindpunt op naar Web PubSub voor Socket.IO en de
room_id
waarde:/*client.js*/ let [socket, editor, room_id] = await initialize('/negotiate');
Wanneer de writer-client is verbonden met de server, verzendt de server een
login
gebeurtenis naar de schrijver. De schrijver kan reageren door de server te vragen zichzelf aan een opgegeven ruimte toe te voegen. Elke 200 milliseconden stuurt de schrijverclient de laatste editorstatus naar de ruimte. Een functie met de naamflush
ordent de verzendlogica./*client.js*/ socket.on("login", () => { updateStatus('Connected'); joinRoom(socket, `${room_id}`); setInterval(() => flush(), 200); // Update editor content // ... });
Als een schrijver geen wijzigingen aanbrengt,
flush()
doet u niets en retourneert u gewoon. Anders worden de wijzigingen in de editorstatus naar de ruimte verzonden./*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(); }
Wanneer een nieuwe viewerclient is verbonden, moet de viewer de meest recente volledige status van de editor krijgen. Hiervoor wordt een bericht met
sync
gegevens verzonden naar de schrijverclient. Het bericht vraagt de schrijverclient om de volledige editorstatus te verzenden./*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-client
Net als de schrijverclient maakt de viewer client zijn Socket.IO client via
initialize()
. Wanneer de viewer-client is verbonden en eenlogin
gebeurtenis van de server ontvangt, wordt de server gevraagd zichzelf aan de opgegeven ruimte te koppelen. De queryroom_id
geeft de ruimte aan./*client.js*/ let [socket, editor] = await initialize(`/register?room_id=${room_id}`) socket.on("login", () => { updateStatus('Connected'); joinRoom(socket, `${room_id}`); });
Wanneer een viewerclient een
message
gebeurtenis van de server ontvangt en het gegevenstype isackJoinRoom
, vraagt de viewerclient de schrijverclient in de ruimte om de volledige editorstatus te verzenden./*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 //... });
Als het gegevenstype is
editorMessage
, werkt de viewerclient de editor bij op basis van de werkelijke inhoud./*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; } } });
Api's van Socket.IO implementeren
joinRoom()
ensendToRoom()
gebruiken:/*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 }); }
De toepassing uitvoeren
De opslagplaats zoeken
De voorgaande secties hebben betrekking op de kernlogica met betrekking tot het synchroniseren van de editorstatus tussen kijkers en de schrijver. U vindt de volledige code in de opslagplaats met voorbeelden.
De opslagplaats klonen
U kunt de opslagplaats klonen en uitvoeren npm install
om projectafhankelijkheden te installeren.
De server starten
node server.js <web-pubsub-connection-string>
Dit is de verbindingsreeks die u in een eerdere stap hebt ontvangen.
Afspelen met de realtime code-editor
Openen http://localhost:3000
op een browsertabblad. Open een ander tabblad met de URL die op de eerste webpagina wordt weergegeven.
Als u code op het eerste tabblad schrijft, ziet u dat het typen in realtime wordt weergegeven op het andere tabblad. Web PubSub voor Socket.IO het doorgeven van berichten in de cloud mogelijk. Uw express
server dient alleen het statische index.html
bestand en het /negotiate
eindpunt.