Vytvoření aplikace pro streamování kódu v reálném čase pomocí Socket.IO a jeho hostování v Azure

Vytváření prostředí v reálném čase, jako je funkce spoluvytování v Microsoft Wordu , může být náročné.

Prostřednictvím snadno použitelných rozhraní API se Socket.IO osvědčila jako knihovna pro komunikaci mezi klienty a serverem v reálném čase. Socket.IO ale uživatelé často hlásí potíže se škálováním připojení Socket.IO. Díky službě Web PubSub pro Socket.IO už se vývojáři nemusí starat o správu trvalých připojení.

Přehled

Tento článek ukazuje, jak vytvořit aplikaci, která umožňuje coderu streamovat aktivity kódování cílové skupině. Tuto aplikaci sestavíte pomocí:

  • Monaco Editor, editor kódu, který využívá Visual Studio Code.
  • Express, webová architektura Node.js.
  • Rozhraní API, která knihovna Socket.IO poskytuje pro komunikaci v reálném čase.
  • Hostování Socket.IO připojení, která pro Socket.IO používají web PubSub.

Dokončená aplikace

Hotová aplikace umožňuje uživateli editoru kódu sdílet webový odkaz, přes který můžou uživatelé sledovat psaní.

Screenshot of the finished code-streaming app.

Tento článek definuje dvě role uživatelů a to, co můžou dělat v editoru, aby byly postupy zaměřené a přehledné přibližně za 15 minut:

  • Autor, který může psát v online editoru a obsah se streamuje
  • Čtenáři, kteří dostávají obsah napsaný v reálném čase a nemůžou obsah upravovat

Architektura

Item Účel Zaměstnanecké výhody
knihovna Socket.IO Poskytuje mechanismus výměny dat s nízkou latencí mezi back-endovou aplikací a klienty. Snadno použitelná rozhraní API, která pokrývají většinu scénářů komunikace v reálném čase
Web PubSub pro Socket.IO Hostuje trvalá připojení typu WebSocket nebo poll-based s klienty Socket.IO. Podpora 100 000 souběžných připojení; Zjednodušená architektura aplikací

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

Předpoklady

Pokud chcete postupovat podle všech kroků v tomto článku, potřebujete:

Vytvoření webového pubSub pro prostředek Socket.IO

K vytvoření prostředku použijte Azure CLI:

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

Získání připojovací řetězec

Připojovací řetězec umožňuje připojení k web pubSub pro Socket.IO.

Spusťte následující příkazy. Nechte vrácenou připojovací řetězec někde, protože ji budete potřebovat při spuštění aplikace později v tomto článku.

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

Napsání kódu na straně serveru aplikace

Začněte psát kód aplikace tím, že pracujete na straně serveru.

Vytvoření serveru HTTP

  1. Vytvoření projektu Node.js:

    mkdir codestream
    cd codestream
    npm init
    
  2. Nainstalujte sadu SDK serveru a Express:

    npm install @azure/web-pubsub-socket.io
    npm install express
    
  3. Naimportujte požadované balíčky a vytvořte server HTTP pro obsluhu statických souborů:

    /*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. Definujte koncový bod s názvem /negotiate. Klient zapisovače nejprve dosáhne tohoto koncového bodu. Tento koncový bod vrátí odpověď HTTP. Odpověď obsahuje koncový bod, který by měl klient použít k navázání trvalého připojení. Vrátí také room hodnotu, ke které je klient přiřazen.

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

Vytvoření webového pubSub pro server Socket.IO

  1. Naimportujte web PubSub pro sadu Socket.IO SDK a definujte možnosti:

    /*server.js*/
    const { useAzureSocketIO } = require("@azure/web-pubsub-socket.io");
    
    const wpsOptions = {
        hub: "codestream",
        connectionString: process.argv[2]
    }
    
  2. Vytvořte web pubSub pro server Socket.IO:

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

Dva kroky se mírně liší od toho, jak byste normálně vytvořili Socket.IO server, jak je popsáno v této Socket.IO dokumentaci. Pomocí těchto dvou kroků může kód na straně serveru přesměrovat správu trvalých připojení ke službě Azure. S pomocí služby Azure funguje aplikační server jenom jako jednoduchý server HTTP.

Implementace obchodní logiky

Teď, když jste vytvořili server Socket.IO hostovaný webem PubSub, můžete definovat, jak klienti a server komunikují pomocí rozhraní API Socket.IO. Tento proces se nazývá implementace obchodní logiky.

  1. Po připojení klienta aplikační server informuje klienta, že je přihlášený odesláním vlastní události s názvem login.

    /*server.js*/
    io.on('connection', socket => {
        socket.emit("login");
    });
    
  2. Každý klient generuje dvě události, na které může server reagovat: joinRoom a sendToRoom. Jakmile server získá room_id hodnotu, ke které se klient chce připojit, použijete socket.join rozhraní API Socket.IO k připojení cílového klienta k zadané místnosti.

    /*server.js*/
    socket.on('joinRoom', async (message) => {
        const room_id = message["room_id"];
        await socket.join(room_id);
    });
    
  3. Po připojení klienta server informuje klienta o úspěšném výsledku odesláním message události. Když klient obdrží message událost s typem ackJoinRoom, klient může požádat server, aby odeslal nejnovější stav editoru.

    /*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. Když klient odešle sendToRoom na server událost, server odešle změny do stavu editoru kódu do zadané místnosti. Všichni klienti v místnosti teď můžou dostávat nejnovější aktualizaci.

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

Napsání kódu na straně klienta aplikace

Po dokončení postupů na straně serveru můžete pracovat na straně klienta.

Počáteční nastavení

Pro komunikaci se serverem je potřeba vytvořit klienta Socket.IO. Otázkou je, se kterým serverem by měl klient navázat trvalé připojení. Vzhledem k tomu, že pro Socket.IO používáte Web PubSub, je server službou Azure. Vzpomeňte si, že jste definovali trasu /negotiate , která klientům obsluhuje koncový bod do webového pubSub pro 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];
}

Funkce initialize(url) organizuje několik operací nastavení dohromady:

  • Načtení koncového bodu do služby Azure ze serveru HTTP
  • Vytvoří instanci Editoru Monaka.
  • Vytvoří trvalé připojení s web pubSub pro Socket.IO

Klient pro zápis

Jak už bylo zmíněno dříve, máte na straně klienta dvě role uživatele: zapisovač a prohlížeč. Cokoli, co autoři zapisují, se streamuje na obrazovku prohlížeče.

  1. Získejte koncový bod do web pubSub pro Socket.IO a room_id hodnotu:

    /*client.js*/
    
    let [socket, editor, room_id] = await initialize('/negotiate');
    
  2. Když je klient zápisu připojen k serveru, server odešle login událost zápisu. Zapisovač může odpovědět tak, že požádá server, aby se připojil k zadané místnosti. Každých 200 milisekund klient zapisovače odešle do místnosti nejnovější stav editoru. Funkce s názvem flush uspořádá odesílající logiku.

    /*client.js*/
    
    socket.on("login", () => {
        updateStatus('Connected');
        joinRoom(socket, `${room_id}`);
        setInterval(() => flush(), 200);
        // Update editor content
        // ...
    });
    
  3. Pokud zapisovač neprovede žádné úpravy, flush() nic nedělá a jednoduše se vrátí. V opačném případě se změny stavu editoru odešlou do místnosti.

    /*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. Když je připojen nový klient prohlížeče, musí prohlížeč získat nejnovější úplný stav editoru. K dosažení tohoto cíle se klientovi zapisovače odešle zpráva obsahující sync data. Zpráva požádá klienta zapisovače, aby odeslal úplný stav editoru.

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

Klient prohlížeče

  1. Stejně jako klient pro zápis vytvoří klient prohlížeče svůj Socket.IO klienta prostřednictvím initialize(). Když je klient prohlížeče připojený a přijme login událost ze serveru, požádá server, aby se připojil k zadané místnosti. room_id Dotaz určuje místnost.

    /*client.js*/
    
    let [socket, editor] = await initialize(`/register?room_id=${room_id}`)
    socket.on("login", () => {
        updateStatus('Connected');
        joinRoom(socket, `${room_id}`);
    });
    
  2. Když klient prohlížeče obdrží message událost ze serveru a datový typ je ackJoinRoom, klient prohlížeče požádá klienta zapisovače v místnosti, aby odeslal úplný stav editoru.

    /*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. Pokud je editorMessagedatový typ , klient prohlížeče aktualizuje editor podle jeho skutečného obsahu.

    /*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. Implementujte joinRoom() a sendToRoom() pomocí rozhraní API 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
        });
    }
    

Spuštění aplikace

Vyhledání úložiště

Předchozí části zahrnovaly základní logiku související se synchronizací stavu editoru mezi diváky a zapisovačem. Kompletní kód najdete v úložišti příkladů.

Klonování úložiště

Úložiště můžete naklonovat a spustit npm install a nainstalovat závislosti projektu.

Spuštění serveru

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

Toto je připojovací řetězec, které jste obdrželi v předchozím kroku.

Přehrávání v editoru kódu v reálném čase

Otevřete http://localhost:3000 na kartě prohlížeče. Otevřete jinou kartu s adresou URL zobrazenou na první webové stránce.

Pokud napíšete kód na první kartě, měli byste vidět, že se psaní projeví v reálném čase na druhé kartě. Web PubSub pro Socket.IO usnadňuje předávání zpráv v cloudu. Váš express server obsluhuje pouze statický index.html soubor a /negotiate koncový bod.