Sdílet prostřednictvím


Kurz: Publikování dat do Socket.IO klientů v bezserverovém režimu ve funkci Azure Functions pomocí Pythonu (Preview)

Tento kurz vás provede publikováním dat klientům Socket.IO v režimu serverless v Pythonu pomocí tvorby aplikace NASDAQ indexu pro reálný čas, integrované s Azure Function.

Vyhledejte úplné ukázky kódu, které se používají v tomto kurzu:

Důležité

Výchozí režim vyžaduje trvalý server. Web PubSub nelze integrovat pro Socket.IO ve výchozím režimu s funkcí Azure Functions.

Požadavky

Vytvořte Web PubSub pro zdroj Socket.IO v serverless režimu

K vytvoření služby Web PubSub pro Socket.IO můžete použít následující příkaz Azure CLI:

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

Vytvoření projektu Azure Functions místně

Měli byste postupovat podle kroků a inicializovat místní projekt funkce Azure Functions.

  1. Podle pokynů nainstalujte nejnovější nástroj azure Functions Core

  2. V okně terminálu nebo z příkazového řádku spusťte následující příkaz, který vytvoří projekt ve SocketIOProject složce:

    func init SocketIOProject --worker-runtime python
    

    Tento příkaz vytvoří projekt funkcí založený na Pythonu. A zadáním složky SocketIOProject spusťte následující příkazy.

  3. V současné době sada funkcí neobsahuje vazbu funkce Socket.IO, a proto musíte balíček přidat ručně.

    1. Pokud chcete odstranit odkaz na sadu funkcí, upravte soubor host.json a odeberte následující řádky.

      "extensionBundle": {
          "id": "Microsoft.Azure.Functions.ExtensionBundle",
          "version": "[4.*, 5.0.0)"
      }
      
    2. Spusťte příkaz:

      func extensions install -p Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO -v 1.0.0-beta.4
      
  4. Nahraďte obsah function_app.py kódy:

    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')
    

    Tady je vysvětlení těchto funkcí:

    • publish_data: Tato funkce aktualizuje NASDAQ index každou sekundu náhodnou změnou a vysílá ji připojeným klientům pomocí Socket.IO výstupního propojení.

    • negotiate: Tato funkce odpoví na výsledek vyjednávání klientovi.

    • index: Tato funkce vrátí statickou stránku HTML.

    Pak přidejte soubor.index.html

    Vytvořte soubor index.html s obsahem:

    <!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>
    

    Klíčová část v 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);
        });
    }
    

    Nejprve vyjednává s aplikací funkcí k získání identifikátoru URI a cesty ke službě. A zaregistrujte zpětné volání pro aktualizaci indexu.

Jak aplikaci spustit místně

Jakmile je kód připravený, postupujte podle pokynů ke spuštění ukázky.

Nastavení služby Azure Storage pro funkci Azure Functions

Azure Functions vyžaduje, aby účet úložiště fungoval i v místním prostředí. Zvolte jednu z těchto dvou možností:

  • Spusťte bezplatný emulátor Azurite.
  • Použijte službu Azure Storage. Pokud ho budete dál používat, mohou vám být účtovány náklady.
  1. Instalace Azurite

    npm install -g azurite
    
  2. Spusťte emulátor úložiště Azurite:

    azurite -l azurite -d azurite\debug.log
    
  3. Ujistěte se, že je AzureWebJobsStorage v local.settings.json nastavena na UseDevelopmentStorage=true.

Nastavte konfiguraci Web PubSub pro Socket.IO

Přidání připojovacího řetězce do aplikace funkcí:

func settings add WebPubSubForSocketIOConnectionString "<connection string>"

Spuštění ukázkové aplikace

Po spuštění nástroje tunelu můžete aplikaci Function App spustit místně:

func start

A navštivte webovou stránku na http://localhost:7071/api/indexadrese .

Snímek obrazovky aplikace

Další kroky

Dále se můžete pokusit pomocí Bicep nasadit aplikaci online s ověřováním na základě identity: