Aracılığıyla paylaş


Socket.IO kullanarak gerçek zamanlı bir kod akışı uygulaması oluşturma ve azure'da barındırma

Microsoft Word'de birlikte oluşturma özelliği gibi gerçek zamanlı bir deneyim oluşturmak zor olabilir.

Kullanımı kolay API'leri sayesinde Socket.IO, istemcilerle sunucu arasında gerçek zamanlı iletişim için bir kitaplık olarak kendini kanıtlamıştır. Ancak Socket.IO kullanıcılar genellikle Socket.IO bağlantılarını ölçeklendirme konusunda zorluk olduğunu bildirir. Socket.IO için Web PubSub ile geliştiricilerin artık kalıcı bağlantıları yönetme konusunda endişelenmesi gerekmez.

Önemli

Ham bağlantı dizesi yalnızca tanıtım amacıyla bu makalede görünür.

bağlantı dizesi, uygulamanızın Azure Web PubSub hizmetine erişmesi için gereken yetkilendirme bilgilerini içerir. bağlantı dizesi içindeki erişim anahtarı, hizmetinizin kök parolasına benzer. Üretim ortamlarında erişim anahtarlarınızı her zaman koruyun. Anahtarlarınızı güvenli bir şekilde yönetmek ve döndürmek ve bağlantınızın WebPubSubServiceClientgüvenliğini sağlamak için Azure Key Vault kullanın.

Erişim anahtarlarını diğer kullanıcılara dağıtmaktan, sabit kodlamaktan veya başkalarının erişebileceği herhangi bir yerde düz metin olarak kaydetmekten kaçının. Ele geçirilmiş olabileceklerini düşünüyorsanız anahtarlarınızı döndürün.

Genel bakış

Bu makalede, bir kodlayıcının hedef kitleye kodlama etkinliklerini akışla aktarmasına olanak tanıyan bir uygulamanın nasıl derlendiğini gösterilmektedir. Bu uygulamayı aşağıdakini kullanarak oluşturursunuz:

  • Visual Studio Code'a güç veren kod düzenleyicisi Monaco Editor.
  • Express, Node.js bir web çerçevesi.
  • Socket.IO kitaplığının gerçek zamanlı iletişim için sağladığı API'ler.
  • Socket.IO için Web PubSub kullanan Socket.IO bağlantıları barındır.

Tamamlanmış uygulama

Tamamlanmış uygulama, bir kod düzenleyicisi kullanıcısının yazma işlemini izleyebilecekleri bir web bağlantısı paylaşmasına olanak tanır.

Tamamlanmış kod akışı uygulamasının ekran görüntüsü.

Yordamları yaklaşık 15 dakika içinde odaklanmış ve sindirilebilir durumda tutmak için, bu makalede iki kullanıcı rolü ve düzenleyicide yapabilecekleri işlemler tanımlanıyor:

  • Çevrimiçi düzenleyicide yazı yazabilen ve içeriğin akışı yapılan bir yazar
  • Yazar tarafından yazılan gerçek zamanlı içeriği alan ve içeriği düzenleyemeyen izleyiciler

Mimari

Kalem Purpose Sosyal haklar
Socket.IO kitaplığı Arka uç uygulaması ve istemcileri arasında düşük gecikme süreli, çift yönlü veri değişimi mekanizması sağlar En gerçek zamanlı iletişim senaryolarını kapsayan kullanımı kolay API'ler
Socket.IO için Web PubSub Socket.IO istemcileriyle WebSocket veya yoklama tabanlı kalıcı bağlantıları barındırıyor 100.000 eşzamanlı bağlantı desteği; basitleştirilmiş uygulama mimarisi

Socket.IO hizmeti için Web PubSub'un istemcileri bir sunucuya nasıl bağladığını gösteren diyagram.

Önkoşullar

Bu makaledeki tüm adımları izlemek için şunlar gerekir:

Socket.IO kaynağı için Web PubSub oluşturma

Kaynağı oluşturmak için Azure CLI'yi kullanın:

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

bağlantı dizesi alma

bağlantı dizesi, Socket.IO için Web PubSub'a bağlanmanızı sağlar.

Aşağıdaki komutları çalıştırın. Döndürülen bağlantı dizesi bir yerde tutun, çünkü uygulamayı bu makalenin ilerleyen bölümlerinde çalıştırdığınızda buna ihtiyacınız olacaktır.

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

Uygulamanın sunucu tarafı kodunu yazma

Sunucu tarafında çalışarak uygulamanızın kodunu yazmaya başlayın.

HTTP sunucusu oluşturma

  1. Node.js projesi oluşturma:

    mkdir codestream
    cd codestream
    npm init
    
  2. Sunucu SDK'sını ve Express'i yükleyin:

    npm install @azure/web-pubsub-socket.io
    npm install express
    
  3. Gerekli paketleri içeri aktarıp statik dosyalara hizmet vermek için bir HTTP sunucusu oluşturun:

    /*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. adlı /negotiatebir uç nokta tanımlayın. Yazıcı istemcisi önce bu uç noktaya isabet eder. Bu uç nokta bir HTTP yanıtı döndürür. Yanıt, istemcinin kalıcı bağlantı kurmak için kullanması gereken bir uç nokta içerir. Ayrıca istemcinin atandığı bir room değer döndürür.

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

Socket.IO sunucusu için Web PubSub oluşturma

  1. Socket.IO SDK için Web PubSub'ı içeri aktarın ve seçenekleri tanımlayın:

    Ham bağlantı dizesi yalnızca tanıtım amacıyla bu makalede görünür. Üretim ortamlarında erişim anahtarlarınızı her zaman koruyun. Anahtarlarınızı güvenli bir şekilde yönetmek ve döndürmek ve bağlantınızın WebPubSubServiceClientgüvenliğini sağlamak için Azure Key Vault kullanın.

    /*server.js*/
    const { useAzureSocketIO } = require("@azure/web-pubsub-socket.io");
    
    const wpsOptions = {
        hub: "codestream",
        connectionString: process.argv[2]
    }
    
  2. Socket.IO sunucusu için Bir Web PubSub oluşturun:

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

İki adım, bu Socket.IO belgelerinde açıklandığı gibi normalde Socket.IO sunucusu oluşturma yönteminizden biraz farklıdır. Bu iki adımla, sunucu tarafı kodunuz bir Azure hizmetine kalıcı bağlantıları yönetme yükünü boşaltabilir. Bir Azure hizmetinin yardımıyla, uygulama sunucunuz yalnızca basit bir HTTP sunucusu gibi davranır.

İş mantığı uygulama

Artık Web PubSub tarafından barındırılan bir Socket.IO sunucusu oluşturduğunuza göre, Socket.IO'nun API'lerini kullanarak istemcilerin ve sunucunun nasıl iletişim kurduğunu tanımlayabilirsiniz. Bu işleme iş mantığı uygulama adı verilir.

  1. Bir istemci bağlandıktan sonra, uygulama sunucusu istemciye adlı loginözel bir olay göndererek oturum açtığını bildirir.

    /*server.js*/
    io.on('connection', socket => {
        socket.emit("login");
    });
    
  2. Her istemci, sunucunun yanıt verebileceği iki olay yayar: joinRoom ve sendToRoom. Sunucu bir istemcinin room_id katılmak istediği değeri aldıktan sonra, hedef istemciyi belirtilen odaya eklemek için Socket.IO'nun API'sinden kullanırsınız socket.join .

    /*server.js*/
    socket.on('joinRoom', async (message) => {
        const room_id = message["room_id"];
        await socket.join(room_id);
    });
    
  3. bir istemci katıldıktan sonra, sunucu bir message olay göndererek başarılı sonucu istemciye bildirir. İstemci türüne ackJoinRoomsahip bir message olay aldığında, istemci sunucudan en son düzenleyici durumunu göndermesini isteyebilir.

    /*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. İstemci sunucuya bir sendToRoom olay gönderdiğinde, sunucu değişiklikleri kod düzenleyicisi durumunda belirtilen odaya yayınlar. Odadaki tüm istemciler artık en son güncelleştirmeyi alabilir.

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

Uygulamanın istemci tarafı kodunu yazma

Artık sunucu tarafı yordamları tamamlandı, istemci tarafında çalışabilirsiniz.

İlk kurulum

Sunucuyla iletişim kurmak için bir Socket.IO istemcisi oluşturmanız gerekir. Soru, istemcinin hangi sunucuyla kalıcı bağlantı kurması gerektiğidir. Socket.IO için Web PubSub kullandığınızdan, sunucu bir Azure hizmetidir. İstemcilere Socket.IO için Web PubSub'a bir uç nokta sunmak üzere bir /negotiate yolu tanımladığını hatırlayın.

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

initialize(url) işlevi birkaç kurulum işlemini birlikte düzenler:

  • HTTP sunucunuzdan uç noktayı bir Azure hizmetine getirir
  • Monaco Düzenleyicisi örneği oluşturur
  • Socket.IO için Web PubSub ile kalıcı bir bağlantı kurar

Yazıcı istemcisi

Daha önce belirtildiği gibi, istemci tarafında iki kullanıcı rolünüz vardır: yazıcı ve görüntüleyici. Yazıcının yazdığını her şey izleyicinin ekranına akışla aktarılır.

  1. Uç noktayı Socket.IO için Web PubSub'a ve değerini alın room_id :

    /*client.js*/
    
    let [socket, editor, room_id] = await initialize('/negotiate');
    
  2. Yazıcı istemcisi sunucuya bağlandığında, sunucu yazıcıya bir login olay gönderir. Yazıcı, sunucudan belirtilen bir odaya katılmasını isteyerek yanıt verebilir. Her 200 milisaniyede bir yazar istemcisi odaya en son düzenleyici durumunu gönderir. adlı flush bir işlev, gönderme mantığını düzenler.

    /*client.js*/
    
    socket.on("login", () => {
        updateStatus('Connected');
        joinRoom(socket, `${room_id}`);
        setInterval(() => flush(), 200);
        // Update editor content
        // ...
    });
    
  3. Yazar herhangi bir düzenleme yapmazsa hiçbir flush() şey yapmaz ve yalnızca döndürür. Aksi takdirde, düzenleyici durumundaki değişiklikler odaya gönderilir.

    /*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. Yeni bir görüntüleyici istemcisi bağlandığında, görüntüleyicinin düzenleyicinin en son tam durumunu alması gerekir. Bunu başarmak için, yazıcı istemcisine veri içeren sync bir ileti gönderilir. İleti, yazıcı istemciden düzenleyici durumunun tamamını göndermesini ister.

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

Görüntüleyici istemcisi

  1. Yazıcı istemcisi gibi görüntüleyici istemcisi de aracılığıyla initialize()Socket.IO istemcisini oluşturur. Görüntüleyici istemcisi bağlandığında ve sunucudan bir login olay aldığında, sunucudan belirtilen odaya katılmasını ister. Sorgu room_id odayı belirtir.

    /*client.js*/
    
    let [socket, editor] = await initialize(`/register?room_id=${room_id}`)
    socket.on("login", () => {
        updateStatus('Connected');
        joinRoom(socket, `${room_id}`);
    });
    
  2. Görüntüleyici istemcisi sunucudan bir message olay aldığında ve veri türü ise ackJoinRoom, görüntüleyici istemcisi odadaki yazıcı istemcisinden düzenleyici durumunun tamamını göndermesini ister.

    /*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. Veri türü ise editorMessage, görüntüleyici istemcisi düzenleyiciyi gerçek içeriğine göre güncelleştirir.

    /*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. Socket.IO'nun API'lerini kullanarak ve sendToRoom() uygulayınjoinRoom():

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

Uygulamayı çalıştırma

Depoyu bulma

Yukarıdaki bölümlerde, görüntüleyiciler ve yazıcı arasında düzenleyici durumunu eşitlemeyle ilgili temel mantık ele alınmıştır. Kodun tamamını örnek deposunda bulabilirsiniz.

Depoyu kopyalama

Depoyu kopyalayıp çalıştırarak npm install proje bağımlılıklarını yükleyebilirsiniz.

Sunucuyu başlatma

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

Bu, önceki bir adımda aldığınız bağlantı dizesi.

Gerçek zamanlı kod düzenleyicisiyle oynama

Tarayıcı sekmesinde açın http://localhost:3000 . İlk web sayfasında URL'nin görüntülendiği başka bir sekme açın.

İlk sekmede kod yazarsanız, yazmanızın diğer sekmede gerçek zamanlı olarak yansıtılmış olduğunu görmeniz gerekir. Socket.IO için Web PubSub, iletinin buluttan geçirilmesini kolaylaştırır. Sunucunuz express yalnızca statik index.html dosyaya ve /negotiate uç noktaya hizmet eder.