Delen via


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.

Schermopname van de voltooide codestreaming-app.

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

Diagram dat laat zien hoe de Web PubSub voor Socket.IO-service clients verbindt met een server.

Vereisten

Als u alle stappen in dit artikel wilt volgen, hebt u het volgende nodig:

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

  1. Een Node.js-project maken:

    mkdir codestream
    cd codestream
    npm init
    
  2. Installeer de server-SDK en Express:

    npm install @azure/web-pubsub-socket.io
    npm install express
    
  3. 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')));
    
  4. 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 een room 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

  1. 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]
    }
    
  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.

  1. Nadat een client is verbonden, vertelt de toepassingsserver de client dat deze is aangemeld door een aangepaste gebeurtenis met de naam loginte verzenden.

    /*server.js*/
    io.on('connection', socket => {
        socket.emit("login");
    });
    
  2. Elke client verzendt twee gebeurtenissen waarop de server kan reageren: joinRoom en sendToRoom. Nadat de server de room_id waarde heeft opgehaald die een client wil toevoegen, gebruikt socket.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);
    });
    
  3. Nadat een client is toegevoegd, informeert de server de client over het geslaagde resultaat door een message gebeurtenis te verzenden. Wanneer de client een message gebeurtenis ontvangt met een type, ackJoinRoomkan 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'});
        }
        // ... 
    });
    
  4. 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.

  1. 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');
    
  2. 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 naam flush ordent de verzendlogica.

    /*client.js*/
    
    socket.on("login", () => {
        updateStatus('Connected');
        joinRoom(socket, `${room_id}`);
        setInterval(() => flush(), 200);
        // Update editor content
        // ...
    });
    
  3. 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();
    }
    
  4. 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

  1. Net als de schrijverclient maakt de viewer client zijn Socket.IO client via initialize(). Wanneer de viewer-client is verbonden en een login gebeurtenis van de server ontvangt, wordt de server gevraagd zichzelf aan de opgegeven ruimte te koppelen. De query room_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}`);
    });
    
  2. Wanneer een viewerclient een message gebeurtenis van de server ontvangt en het gegevenstype is ackJoinRoom, 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 //...
    });
    
  3. 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;
            }
        }
    });
    
  4. Api's van Socket.IO implementeren joinRoom() en sendToRoom() 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.