Udostępnij za pomocą


Samouczek: publikowanie danych na klientach Socket.IO w trybie bezserwerowym w funkcji platformy Azure przy użyciu języka Python (wersja zapoznawcza)

W tym samouczku przedstawiono sposób publikowania danych na Socket.IO klientach w trybie bezserwerowym w języku Python przez utworzenie aplikacji indeksu NASDAQ w czasie rzeczywistym zintegrowanej z funkcją platformy Azure.

Znajdź pełne przykłady kodu używane w tym samouczku:

Ważne

Tryb domyślny wymaga trwałego serwera. Nie można zintegrować usługi Web PubSub dla Socket.IO w trybie domyślnym z funkcją platformy Azure.

Wymagania wstępne

Tworzenie usługi Web PubSub dla zasobu Socket.IO w trybie bezserwerowym

Aby utworzyć usługę Web PubSub dla Socket.IO, możesz użyć następującego polecenia interfejsu wiersza polecenia platformy Azure :

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

Lokalne tworzenie projektu funkcji platformy Azure

Należy wykonać kroki, aby zainicjować lokalny projekt funkcji platformy Azure.

  1. Wykonaj kroki, aby zainstalować najnowsze narzędzie podstawowe funkcji platformy Azure

  2. W oknie terminalu lub w wierszu polecenia uruchom następujące polecenie, aby utworzyć projekt w folderze SocketIOProject :

    func init SocketIOProject --worker-runtime python
    

    To polecenie tworzy projekt funkcji oparty na języku Python. Przejdź do folderu SocketIOProject, aby uruchomić następujące polecenia.

  3. Obecnie pakiet funkcji nie zawiera Socket.IO powiązania funkcji, dlatego należy ręcznie dodać pakiet.

    1. Aby wyeliminować odwołanie do pakietu funkcji, zmodyfikuj plik host.json i usuń następujące wiersze.

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

      func extensions install -p Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO -v 1.0.0-beta.4
      
  4. Zastąp zawartość w function_app.py pliku kodami:

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

    Oto wyjaśnienie tych funkcji:

    • publish_data: Ta funkcja aktualizuje indeks NASDAQ co sekundę za pomocą losowej zmiany i emituje go do połączonych klientów z wyjściowym bindigiem Socket.IO.

    • negotiate: Ta funkcja odpowiada na wynik negocjacji dla klienta.

    • index: Ta funkcja zwraca statyczną stronę HTML.

    Następnie dodaj index.html plik

    Utwórz plik index.html z zawartością:

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

    Kluczowa część w 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);
        });
    }
    

    Najpierw negocjuje z aplikacją Function App, aby uzyskać URI i ścieżkę do usługi. Zarejestruj wywołanie zwrotne w celu zaktualizowania indeksu.

Jak uruchomić aplikację lokalnie

Po przygotowaniu kodu wykonaj instrukcje uruchamiania przykładu.

Konfigurowanie usługi Azure Storage dla funkcji platformy Azure

Usługa Azure Functions wymaga konta magazynu do działania, nawet gdy jest uruchomiona lokalnie. Wybierz jedną z dwóch następujących opcji:

  • Uruchom bezpłatny emulator Azurite.
  • Użyj usługi Azure Storage. Może to wiązać się z kosztami, jeśli będzie on nadal używany.
  1. Instalowanie Azurite

    npm install -g azurite
    
  2. Uruchom emulator magazynu Azurite:

    azurite -l azurite -d azurite\debug.log
    
  3. Upewnij się, że AzureWebJobsStorage w local.settings.json ustawiono wartość UseDevelopmentStorage=true.

Ustawienie konfiguracji usługi Web PubSub dla Socket.IO

Dodaj ciąg połączenia do aplikacji Function.

func settings add WebPubSubForSocketIOConnectionString "<connection string>"

Uruchamianie przykładowej aplikacji

Po uruchomieniu narzędzia tunelu możesz uruchomić aplikację funkcji lokalnie:

func start

Odwiedź stronę internetową pod adresem http://localhost:7071/api/index.

Zrzut ekranu przedstawiający aplikację.

Dalsze kroki

Następnie możesz spróbować użyć aplikacji Bicep do wdrożenia aplikacji w trybie online przy użyciu uwierzytelniania opartego na tożsamościach: