Share via


Valós idejű kódstreamelési alkalmazás létrehozása Socket.IO használatával és az Azure-ban való üzemeltetésével

A Microsoft Word együttes létrehozási funkciójával hasonló valós idejű élmény létrehozása kihívást jelenthet.

A könnyen használható API-kkal Socket.IO az ügyfelek és a kiszolgálók közötti valós idejű kommunikáció kódtáraként bizonyult. Azonban Socket.IO felhasználók gyakran jelentenek nehézséget a Socket.IO kapcsolatainak skálázásával kapcsolatban. A Web PubSub for Socket.IO esetében a fejlesztőknek már nem kell aggódniuk az állandó kapcsolatok kezelése miatt.

Áttekintés

Ez a cikk bemutatja, hogyan hozhat létre olyan alkalmazást, amely lehetővé teszi a kódoló számára, hogy kódolási tevékenységeket streameljen a közönségnek. Ezt az alkalmazást a következő használatával hozhatja létre:

  • Monaco Editor, a Visual Studio Code-ot irányító kódszerkesztő.
  • Express, egy Node.js webes keretrendszer.
  • A Socket.IO kódtár által biztosított API-k valós idejű kommunikációt biztosítanak.
  • Gazdagép Socket.IO kapcsolatok, amelyek a Web PubSub-t használják Socket.IO.

A kész alkalmazás

A kész alkalmazás lehetővé teszi a kódszerkesztő felhasználójának, hogy megossza egy webes hivatkozást, amelyen keresztül a felhasználók megnézhetik a gépelést.

Screenshot of the finished code-streaming app.

Az eljárások körülbelül 15 perc alatt történő összpontosítása és emészthetősége érdekében ez a cikk két felhasználói szerepkört és a szerkesztőben elvégezhető műveleteket határozza meg:

  • Egy író, aki be tud gépelni az online szerkesztőbe, és a tartalom streamelve van
  • Azok a megtekintők, akik valós idejű, az író által beírt tartalmat kapnak, és nem tudják szerkeszteni a tartalmat

Architektúra

Cikk Purpose Benefits
Socket.IO könyvtár Alacsony késésű, kétirányú adatcsere mechanizmust biztosít a háttéralkalmazás és az ügyfelek között Könnyen használható API-k, amelyek a legtöbb valós idejű kommunikációs forgatókönyvet lefedik
Web PubSub for Socket.IO WebSocket- vagy lekérdezésalapú állandó kapcsolatokat üzemeltet Socket.IO ügyfelekkel 100 000 egyidejű kapcsolat támogatása; egyszerűsített alkalmazásarchitektúra

Diagram that shows how the Web PubSub for Socket.IO service connects clients with a server.

Előfeltételek

A cikk összes lépésének követéséhez a következőkre van szüksége:

Web PubSub létrehozása Socket.IO erőforráshoz

Az erőforrás létrehozásához használja az Azure CLI-t:

az webpubsub create -n <resource-name> \
                    -l <resource-location> \
                    -g <resource-group> \
                    --kind SocketIO \
                    --sku Free_F1

Kapcsolati sztring lekérése

A kapcsolati sztring lehetővé teszi a Web PubSub-hez való kapcsolódást Socket.IO.

Futtassa az alábbi parancsokat. Tartsa valahol a visszaadott kapcsolati sztring, mert szüksége lesz rá, amikor a cikk későbbi részében futtatja az alkalmazást.

az webpubsub key show -n <resource-name> \ 
                      -g <resource-group> \ 
                      --query primaryKey \
                      -o tsv

Az alkalmazás kiszolgálóoldali kódjának írása

Kezdje el írni az alkalmazás kódját a kiszolgáló oldalán.

HTTP-kiszolgáló létrehozása

  1. Node.js-projekt létrehozása:

    mkdir codestream
    cd codestream
    npm init
    
  2. Telepítse a kiszolgálóIDK-t és az Expresst:

    npm install @azure/web-pubsub-socket.io
    npm install express
    
  3. Importálja a szükséges csomagokat, és hozzon létre egy HTTP-kiszolgálót a statikus fájlok kiszolgálásához:

    /*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. Adjon meg egy .-nek nevezett végpontot /negotiate. Először egy íróügyfél éri el ezt a végpontot. Ez a végpont HTTP-választ ad vissza. A válasz tartalmaz egy végpontot, amelyet az ügyfélnek egy állandó kapcsolat létrehozásához kell használnia. Azt az értéket is visszaadja room , amelyhez az ügyfél hozzá van rendelve.

    /*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);
    });
    

A Web PubSub létrehozása Socket.IO kiszolgálóhoz

  1. Importálja a Web PubSubot Socket.IO SDK-hoz, és adja meg a beállításokat:

    /*server.js*/
    const { useAzureSocketIO } = require("@azure/web-pubsub-socket.io");
    
    const wpsOptions = {
        hub: "codestream",
        connectionString: process.argv[2]
    }
    
  2. Web PubSub létrehozása Socket.IO kiszolgálóhoz:

    /*server.js*/
    
    const io = require("socket.io")();
    useAzureSocketIO(io, wpsOptions);
    

A két lépés kissé eltér a Socket.IO-kiszolgáló létrehozásának módjától, ahogyan azt ebben a Socket.IO dokumentációban ismertetjük. Ezzel a két lépéssel a kiszolgálóoldali kód ki tudja kapcsolni egy Azure-szolgáltatás állandó kapcsolatainak kezelését. Egy Azure-szolgáltatás segítségével az alkalmazáskiszolgáló csak egy egyszerű HTTP-kiszolgálóként működik.

Üzleti logika megvalósítása

Most, hogy létrehozott egy Socket.IO-kiszolgálót, amelyet a Web PubSub üzemeltet, meghatározhatja, hogyan kommunikálnak az ügyfelek és a kiszolgáló a Socket.IO API-jaival. Ezt a folyamatot üzleti logika implementálásának nevezzük.

  1. Az ügyfél csatlakoztatása után az alkalmazáskiszolgáló egy egyéni, névvel ellátott loginesemény küldésével tájékoztatja az ügyfelet arról, hogy be van jelentkezve.

    /*server.js*/
    io.on('connection', socket => {
        socket.emit("login");
    });
    
  2. Minden ügyfél két eseményt bocsát ki, amelyekre a kiszolgáló válaszolhat: joinRoom és sendToRoom. Miután a kiszolgáló megkapja azt az room_id értéket, amelyhez egy ügyfél csatlakozni szeretne, a socket.join Socket.IO API-jával csatlakozhat a célügyfélhez a megadott helyiséghez.

    /*server.js*/
    socket.on('joinRoom', async (message) => {
        const room_id = message["room_id"];
        await socket.join(room_id);
    });
    
  3. Az ügyfél csatlakoztatása után a kiszolgáló egy esemény küldésével tájékoztatja az ügyfelet message a sikeres eredményről. Amikor az ügyfél egy ilyen típusú ackJoinRoomeseményt message kap, az ügyfél megkérheti a kiszolgálót, hogy küldje el a legújabb szerkesztőállapotot.

    /*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. Amikor egy ügyfél eseményt sendToRoom küld a kiszolgálónak, a kiszolgáló a kódszerkesztő állapotának módosításait a megadott helyiségbe továbbítja. A helyiség összes ügyfele megkapja a legújabb frissítést.

    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
        });
    });
    

Az alkalmazás ügyféloldali kódjának írása

Most, hogy a kiszolgálóoldali eljárások befejeződnek, az ügyféloldalon is dolgozhat.

Kezdeti beállítás

Létre kell hoznia egy Socket.IO ügyfelet a kiszolgálóval való kommunikációhoz. A kérdés az, hogy melyik kiszolgálóval kell állandó kapcsolatot létesítenie az ügyfélnek. Mivel a Web PubSub-t használja Socket.IO, a kiszolgáló egy Azure-szolgáltatás. Ne feledje, hogy definiált egy /negotiate útvonalat, amely az ügyfelek számára végpontot biztosít a Web PubSub számára 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];
}

A initialize(url) függvény néhány beállítási műveletet együtt rendszerez:

  • Lekéri a végpontot egy Azure-szolgáltatásba a HTTP-kiszolgálóról
  • Monaco Editor-példány létrehozása
  • Állandó kapcsolatot hoz létre a Web PubSub szolgáltatással a Socket.IO

Íróügyfél

Ahogy korábban említettük, két felhasználói szerepköre van az ügyféloldalon: író és megtekintő. Minden, amit az író típusok streamelnek a megjelenítő képernyőjére.

  1. Kérje le a végpontot a Web PubSubhoz Socket.IO és az room_id értékhez:

    /*client.js*/
    
    let [socket, editor, room_id] = await initialize('/negotiate');
    
  2. Amikor az íróügyfél csatlakozik a kiszolgálóhoz, a kiszolgáló eseményt login küld az írónak. Az író úgy válaszolhat, hogy megkéri a kiszolgálót, hogy csatlakozzon egy adott helyiséghez. Minden 200 ezredmásodpercben az íróügyfél elküldi a legújabb szerkesztői állapotot a szobába. Egy elnevezett flush függvény rendszerezi a küldési logikát.

    /*client.js*/
    
    socket.on("login", () => {
        updateStatus('Connected');
        joinRoom(socket, `${room_id}`);
        setInterval(() => flush(), 200);
        // Update editor content
        // ...
    });
    
  3. Ha egy író nem végez módosításokat, nem tesz semmit, flush() és egyszerűen visszatér. Ellenkező esetben a szerkesztő állapotának módosításait a rendszer elküldi a helyiségnek.

    /*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. Ha egy új megtekintő ügyfél csatlakozik, a megtekintőnek a szerkesztő legújabb teljes állapotát kell megkapnia. Ennek eléréséhez a rendszer adatokat tartalmazó sync üzenetet küld az íróügyfélnek. Az üzenet arra kéri az íróügyfélt, hogy küldje el a teljes szerkesztői állapotot.

    /*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,
            });
        }
    });
    

Megtekintő ügyfél

  1. Az íróügyfélhöz hasonlóan a megtekintő ügyfél is létrehozza a Socket.IO-ügyfelet.initialize() Amikor a megtekintő ügyfél csatlakoztatva van, és eseményt login kap a kiszolgálótól, felkéri a kiszolgálót, hogy csatlakozzon a megadott helyiséghez. A lekérdezés room_id megadja a helyiséget.

    /*client.js*/
    
    let [socket, editor] = await initialize(`/register?room_id=${room_id}`)
    socket.on("login", () => {
        updateStatus('Connected');
        joinRoom(socket, `${room_id}`);
    });
    
  2. Amikor egy megtekintő ügyfél eseményt message kap a kiszolgálótól, és az adattípus az ackJoinRoom, a megtekintő ügyfél megkéri a szobában lévő íróügyfélt, hogy küldje el a teljes szerkesztőállapotot.

    /*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. Ha az adattípus az editorMessage, a megtekintő ügyfél a tényleges tartalomnak megfelelően frissíti a szerkesztőt .

    /*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. Implementálja joinRoom() és sendToRoom() használja a Socket.IO API-jait:

    /*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
        });
    }
    

Az alkalmazás futtatása

Az adattár megkeresése

Az előző szakaszok a szerkesztő állapotának a megtekintők és az író közötti szinkronizálásával kapcsolatos alapvető logikát fedik le. A teljes kódot a példák adattárában találja.

Az adattár klónozása

Klónozhatja az adattárat, és futtathatja npm install a projektfüggőségek telepítéséhez.

A kiszolgáló indítása

node server.js <web-pubsub-connection-string>

Ez az a kapcsolati sztring, amelyet egy korábbi lépésben kapott.

Lejátszás a valós idejű kódszerkesztővel

Megnyitás http://localhost:3000 böngészőlapon. Nyisson meg egy másik lapot az első weblapon megjelenő URL-címmel.

Ha az első lapon kódot ír, akkor a gépelés valós időben jelenik meg a másik lapon. A Web PubSub for Socket.IO megkönnyíti az üzenetek felhőben való továbbítását. A express kiszolgáló csak a statikus index.html fájlt és a végpontot /negotiate szolgálja ki.