Bagikan melalui


Tutorial: Menerbitkan data ke klien Socket.IO dalam Mode Tanpa Server di Azure Function dengan Python (Pratinjau)

Tutorial ini memandu Anda melalui cara menerbitkan data ke klien Socket.IO dalam Mode Tanpa Server di Python dengan membuat aplikasi indeks NASDAQ real time yang terintegrasi dengan Azure Function.

Temukan sampel kode lengkap yang digunakan dalam tutorial ini:

Penting

Mode Default memerlukan server persisten, Anda tidak dapat mengintegrasi Web PubSub untuk Socket.IO dalam mode default dengan Azure Function.

Prasyarat

Membuat Web PubSub untuk sumber daya Socket.IO dalam Mode Tanpa Server

Untuk membuat Web PubSub untuk Socket.IO, Anda bisa menggunakan perintah Azure CLI berikut:

az webpubsub create -g <resource-group> -n <resource-name>---kind socketio --service-mode serverless --sku Premium_P1

Membuat proyek Azure Function secara lokal

Anda harus mengikuti langkah-langkah untuk memulai proyek Azure Function lokal.

  1. Ikuti langkah untuk menginstal alat inti Azure Function terbaru

  2. Di jendela terminal atau dari prompt perintah, jalankan perintah berikut untuk membuat proyek di SocketIOProject folder:

    func init SocketIOProject --worker-runtime python
    

    Perintah ini membuat proyek Fungsi berbasis Python. Dan masukkan folder SocketIOProject untuk menjalankan perintah berikut.

  3. Saat ini, Function Bundle tidak menyertakan Socket.IO Function Binding, jadi Anda perlu menambahkan paket secara manual.

    1. Untuk menghilangkan referensi bundel fungsi, edit file host.json dan hapus baris berikut.

      "extensionBundle": {
          "id": "Microsoft.Azure.Functions.ExtensionBundle",
          "version": "[4.*, 5.0.0)"
      }
      
    2. Jalankan perintah:

      func extensions install -p Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO -v 1.0.0-beta.4
      
  4. Ganti konten dalam function_app.py dengan kode:

    import random
    import azure.functions as func
    from azure.functions.decorators.core import DataType
    from azure.functions import Context
    import json
    
    app = func.FunctionApp()
    current_index= 14000
    
    @app.timer_trigger(schedule="* * * * * *", arg_name="myTimer", run_on_startup=False,
                use_monitor=False)
    @app.generic_output_binding("sio", type="socketio", data_type=DataType.STRING, hub="hub")
    def publish_data(myTimer: func.TimerRequest,
                    sio: func.Out[str]) -> None:
        change = round(random.uniform(-10, 10), 2)
        global current_index
        current_index = current_index + change
        sio.set(json.dumps({
            'actionName': 'sendToNamespace',
            'namespace': '/',
            'eventName': 'update',
            'parameters': [
                current_index
            ]
        }))
    
    @app.function_name(name="negotiate")
    @app.route(auth_level=func.AuthLevel.ANONYMOUS)
    @app.generic_input_binding("negotiationResult", type="socketionegotiation", hub="hub")
    def negotiate(req: func.HttpRequest, negotiationResult) -> func.HttpResponse:
        return func.HttpResponse(negotiationResult)
    
    @app.function_name(name="index")
    @app.route(auth_level=func.AuthLevel.ANONYMOUS)
    def index(req: func.HttpRequest) -> func.HttpResponse:
        path = './index.html'
        with open(path, 'rb') as f:
            return func.HttpResponse(f.read(), mimetype='text/html')
    

    Berikut penjelasan fungsi-fungsi ini:

    • publish_data: Fungsi ini memperbarui indeks NASDAQ setiap detik dengan perubahan acak dan menyiarkannya ke klien yang terhubung dengan pengikatan keluaran Socket.IO.

    • negotiate: Fungsi ini menanggapi hasil negosiasi kepada klien.

    • index: Fungsi ini mengembalikan halaman HTML statis.

    Kemudian tambahkan index.html file

    Buat file index.html dengan konten:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Nasdaq Index</title>
        <style>
            /* Reset some default styles */
            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }
    
            body {
                font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
                background: linear-gradient(135deg, #f5f7fa, #c3cfe2);
                height: 100vh;
                display: flex;
                justify-content: center;
                align-items: center;
            }
    
            .container {
                background-color: white;
                padding: 40px;
                border-radius: 12px;
                box-shadow: 0 4px 6px rgba(0,0,0,0.1);
                text-align: center;
                max-width: 300px;
                width: 100%;
            }
    
            .nasdaq-title {
                font-size: 2em;
                color: #003087;
                margin-bottom: 20px;
            }
    
            .index-value {
                font-size: 3em;
                color: #16a34a;
                margin-bottom: 30px;
                transition: color 0.3s ease;
            }
    
            .update-button {
                padding: 10px 20px;
                font-size: 1em;
                color: white;
                background-color: #003087;
                border: none;
                border-radius: 6px;
                cursor: pointer;
                transition: background-color 0.3s ease;
            }
    
            .update-button:hover {
                background-color: #002070;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <div class="nasdaq-title">STOCK INDEX</div>
            <div id="nasdaqIndex" class="index-value">14,000.00</div>
        </div>
    
        <script src="https://cdn.socket.io/4.7.5/socket.io.min.js"></script>
        <script>
            function updateIndexCore(newIndex) {
                newIndex = parseFloat(newIndex);
                currentIndex = parseFloat(document.getElementById('nasdaqIndex').innerText.replace(/,/g, ''))
                change = newIndex - currentIndex;
                // Update the index value in the DOM
                document.getElementById('nasdaqIndex').innerText = newIndex.toLocaleString('en-US', {minimumFractionDigits: 2, maximumFractionDigits: 2});
    
                // Optionally, change the color based on increase or decrease
                const indexElement = document.getElementById('nasdaqIndex');
                if (change > 0) {
                    indexElement.style.color = '#16a34a'; // Green for increase
                } else if (change < 0) {
                    indexElement.style.color = '#dc2626'; // Red for decrease
                } else {
                    indexElement.style.color = '#16a34a'; // Neutral color
                }
            }
    
            async function init() {
                const negotiateResponse = await fetch(`/api/negotiate`);
                if (!negotiateResponse.ok) {
                    console.log("Failed to negotiate, status code =", negotiateResponse.status);
                    return;
                }
                const negotiateJson = await negotiateResponse.json();
                socket = io(negotiateJson.endpoint, {
                    path: negotiateJson.path,
                    query: { access_token: negotiateJson.token}
                });
    
                socket.on('update', (index) => {
                    updateIndexCore(index);
                });
            }
    
            init();
        </script>
    </body>
    </html>
    

    Bagian kunci dalam index.html:

    async function init() {
        const negotiateResponse = await fetch(`/api/negotiate`);
        if (!negotiateResponse.ok) {
            console.log("Failed to negotiate, status code =", negotiateResponse.status);
            return;
        }
        const negotiateJson = await negotiateResponse.json();
        socket = io(negotiateJson.endpoint, {
            path: negotiateJson.path,
            query: { access_token: negotiateJson.token}
        });
    
        socket.on('update', (index) => {
            updateIndexCore(index);
        });
    }
    

    Ini pertama-tama bernegosiasi dengan Aplikasi Fungsi untuk mendapatkan Uri dan jalur ke layanan. Dan daftarkan panggilan balik untuk memperbarui indeks.

Cara menjalankan Aplikasi secara lokal

Setelah kode disiapkan, ikuti instruksi untuk menjalankan sampel.

Siapkan Azure Storage untuk Fungsi Azure.

Azure Functions memerlukan akun penyimpanan untuk berfungsi bahkan ketika berjalan secara lokal. Pilih salah satu dari dua opsi berikut:

  • Jalankan emulator Azurite gratis.
  • Gunakan layanan Azure Storage. Ini mungkin dikenakan biaya jika Anda terus menggunakannya.
  1. Pemasangan Program Azurite

    npm install -g azurite
    
  2. Mulai emulator penyimpanan Azurite:

    azurite -l azurite -d azurite\debug.log
    
  3. Pastikan AzureWebJobsStorage dalam local.settings.json diatur ke UseDevelopmentStorage=true.

Menyiapkan konfigurasi Web PubSub untuk Socket.IO

Tambahkan string koneksi ke Aplikasi Fungsi:

func settings add WebPubSubForSocketIOConnectionString "<connection string>"

Jalankan Aplikasi Sampel

Setelah alat terowongan berjalan, Anda dapat menjalankan Aplikasi Fungsi secara lokal:

func start

Dan kunjungi halaman web di http://localhost:7071/api/index.

Cuplikan layar aplikasi.

Langkah selanjutnya

Selanjutnya, Anda dapat mencoba menggunakan Bicep untuk menyebarkan aplikasi secara online dengan autentikasi berbasis identitas: