次の方法で共有


チュートリアル: Python を使用して Azure 関数のサーバーレス モードで Socket.IO クライアントにデータを発行する (プレビュー)

このチュートリアルでは、Azure Function と統合されたリアルタイム NASDAQ インデックス アプリケーションを作成して、Python のサーバーレス モードでデータを Socket.IO クライアントに発行する方法について説明します。

このチュートリアルで使用されている完全なコード サンプルを見つけます。

Important

既定モードには永続的なサーバーが必要です。既定モードの web PubSub for Socket.IO を Azure 関数と統合することはできません。

[前提条件]

サーバーレス モードで Socket.IO リソース用の Web PubSub を作成する

Socket.IO 用の Web PubSub を作成するには、次の Azure CLI コマンドを使用できます。

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

Azure 関数プロジェクトをローカルに作成する

手順に従って、ローカルの Azure 関数プロジェクトを開始する必要があります。

  1. 手順に従って、最新の Azure 関数コア ツールをインストールします

  2. ターミナル ウィンドウまたはコマンド プロンプトで次のコマンドを実行して、SocketIOProject フォルダーにプロジェクトを作成します。

    func init SocketIOProject --worker-runtime python
    

    このコマンドは、Python ベースの関数プロジェクトを作成します。 次のコマンドを実行するには、フォルダー SocketIOProject を入力します。

  3. 現時点では、関数バンドルには関数バインド Socket.IO 含まれていないため、パッケージを手動で追加する必要があります。

    1. 関数バンドル参照を削除するには、host.json ファイルを編集し、次の行を削除します。

      "extensionBundle": {
          "id": "Microsoft.Azure.Functions.ExtensionBundle",
          "version": "[4.*, 5.0.0)"
      }
      
    2. 次のコマンドを実行します。

      func extensions install -p Microsoft.Azure.WebJobs.Extensions.WebPubSubForSocketIO -v 1.0.0-beta.4
      
  4. function_app.py内のコンテンツを次のコードに置き換えます。

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

    これらの関数の説明を次に示します。

    • publish_data:この関数は、NASDAQインデックスをランダムな変更で毎秒更新し、Socket.IO 出力バインディングを使用して接続されたクライアントにブロードキャストします。

    • negotiate: この関数は、ネゴシエーションの結果をクライアントに応答します。

    • index: この関数は静的 HTML ページを返します。

    次に、 index.html ファイルを追加します

    コンテンツを含む index.html ファイルを作成します。

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

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

    最初に関数アプリとネゴシエートして、Uri とサービスへのパスを取得します。 インデックスを更新するためのコールバックを登録します。

アプリをローカルで実行する方法

コードの準備が完了したら、手順に従ってサンプルを実行します。

Azure 関数用に Azure Storage を設定する

Azure Functions では、ローカルで実行されている場合でも、ストレージ アカウントが機能する必要があります。 次の 2 つのオプションのいずれかを選択します。

  • 無料の Azurite エミュレーターを実行します。
  • Azure Storage サービスを使用します。 引き続き使用すると、コストが発生する可能性があります。
  1. Azurite をインストールする

    npm install -g azurite
    
  2. Azurite ストレージ エミュレーターを起動します。

    azurite -l azurite -d azurite\debug.log
    
  3. local.settings.jsonのAzureWebJobsStorageUseDevelopmentStorage=trueに設定されていることを確認します。

Socket.IO の Web PubSub の構成を設定する

Function APP に接続文字列を追加します。

func settings add WebPubSubForSocketIOConnectionString "<connection string>"

サンプル アプリの実行

トンネル ツールが実行されたら、関数アプリをローカルで実行できます。

func start

また、 http://localhost:7071/api/indexの Web ページにアクセスしてください。

アプリのスクリーンショット。

次のステップ

次に、Bicep を使用して、ID ベースの認証を使用してアプリをオンラインでデプロイできます。