Azure Web PubSub でサポートされる Protobuf WebSocket サブプロトコル

このドキュメントでは、サブプロトコル protobuf.webpubsub.azure.v1 について説明します。

クライアントがこのサブプロトコルを使用している場合、送信データ フレームと受信データ フレームの両方がプロトコル バッファー (protobuf) ペイロードである必要があります。

概要

サブプロトコル protobuf.webpubsub.azure.v1 を使用すると、クライアントでは、上流サーバーへのラウンド トリップではなく、直接の発行またはサブスクライブ (PubSub) を行うことができます。 protobuf.webpubsub.azure.v1 サブプロトコルを持つ WebSocket 接続を、PubSub WebSocket クライアントと呼びます。

たとえば、JavaScript では、次のコードを使用して、protobuf サブプロトコルを使用する PubSub WebSocket クライアントを作成できます。

// PubSub WebSocket client
var pubsub = new WebSocket('wss://test.webpubsub.azure.com/client/hubs/hub1', 'protobuf.webpubsub.azure.v1');

シンプルな WebSocket クライアントの場合、サーバーにはクライアントからイベントを処理するために必要なロールがあります。 シンプルな WebSocket 接続では、メッセージを送信するときには必ず message イベントをトリガーします。また、常にサーバー側に依存して、メッセージを処理したり他の操作を行ったりします。 protobuf.webpubsub.azure.v1 サブプロトコルを使用することで、許可されているクライアントは、参加要求を使用してグループに参加し、発行要求を直接使用してグループにメッセージを発行できます。 クライアントは、イベント要求を使用して、メッセージが属するイベントをカスタマイズすることで、さまざまな上流イベント ハンドラにメッセージをルーティングすることもできます。

Note

現在、Web PubSub サービスは proto3 のみをサポートしています。

アクセス許可

PubSub WebSocket クライアントは、許可されている場合にのみ他のクライアントに発行できます。 クライアントに割り当てられた roles によって、そのクライアントに付与されるアクセス許可が決まります。

役割 アクセス許可
指定なし クライアントは、イベント要求を送信できます。
webpubsub.joinLeaveGroup クライアントは、どのグループについても、参加と脱退が可能です。
webpubsub.sendToGroup クライアントは、どのグループにもメッセージを発行できます。
webpubsub.joinLeaveGroup.<group> クライアントはグループ <group> について、参加と脱退が可能です。
webpubsub.sendToGroup.<group> クライアントはグループ <group> にメッセージを発行できます。
webpubsub.joinLeaveGroups.<pattern> クライアントは、名前が <pattern> と一致するすべてのグループに参加または脱退できます ( ワイルドカード グループの役割パターンを参照)。
webpubsub.sendToGroups.<pattern> クライアントは、名前が <pattern> と一致する任意のグループにメッセージを発行できます ( ワイルドカード グループの役割パターンを参照)。

REST API またはサーバー SDK を使用して、サーバーからクライアントのアクセス許可を動的に付与または取り消すことができます。

Note

ワイルドカード ロール ( webpubsub.sendToGroups.<pattern> など) は、実行時に REST API またはサーバー SDK ではまだサポートされていません。

要求

すべての要求メッセージは、次の protobuf 形式に従います。

syntax = "proto3";

import "google/protobuf/any.proto";

message UpstreamMessage {
    oneof message {
        SendToGroupMessage send_to_group_message = 1;
        EventMessage event_message = 5;
        JoinGroupMessage join_group_message = 6;
        LeaveGroupMessage leave_group_message = 7;
        SequenceAckMessage sequence_ack_message = 8;
        PingMessage ping_message = 9;
        StreamDataMessage stream_data_message = 13;
        StreamEndMessage stream_end_message = 14;
    }

    message SendToGroupMessage {
        string group = 1;
        optional uint64 ack_id = 2;
        MessageData data = 3;
        optional bool no_echo = 4;
        StreamStartInfo stream = 7;
    }

    message StreamStartInfo {
        string stream_id = 1;
        optional uint32 idle_timeout_ms = 2;
    }

    message EventMessage {
        string event = 1;
        MessageData data = 2;
        optional uint64 ack_id = 3;
    }
    
    message JoinGroupMessage {
        string group = 1;
        optional uint64 ack_id = 2;
    }

    message LeaveGroupMessage {
        string group = 1;
        optional uint64 ack_id = 2;
    }

    message SequenceAckMessage {
        uint64 sequence_id = 1;
    }

    message PingMessage {
    }

    message StreamDataMessage {
        string stream_id = 1;
        optional uint64 stream_sequence_id = 2;
        MessageData data = 3;
    }

    message StreamEndMessage {
        string stream_id = 1;
        optional StreamEndError error = 2;

        message StreamEndError {
            optional string message = 1;
            optional string user_error_code = 2;
        }
    }
}

message MessageData {
    oneof data {
        string text_data = 1;
        bytes binary_data = 2;
        google.protobuf.Any protobuf_data = 3;
    }
}

グループに参加する

形式:

join_group_message.group をグループ名に設定します。

  • ackId は各要求の ID で、一意である必要があります。 要求の処理結果を通知するために、サービスから確認応答メッセージが送信されます。 詳細については、AckId と確認応答に関するページを参照してください。

グループを脱退する

形式:

leave_group_message.group をグループ名に設定します。

  • ackId は各要求の ID で、一意である必要があります。 要求の処理結果を通知するために、サービスから確認応答メッセージが送信されます。 詳細については、AckId と確認応答に関するページを参照してください。

メッセージを発行する

形式:

  • ackId: 各要求の一意の ID。 要求の処理結果を通知するために、サービスから確認応答メッセージが送信されます。 詳細については、AckId と確認応答に関するページを参照してください。

  • dataType: protobuftextに応じて、binarydata 、または MessageData であるデータ形式。 受信側のクライアントは、 dataType を使用してコンテンツを正しく処理できます。

  • protobuf: send_to_group_message.data.protobuf_data を設定した場合、暗黙的な dataTypeprotobuf です。 protobuf_data は、任意のメッセージ型にすることができます。 他のすべてのクライアントは protobuf でエンコードされたバイナリを受け取り、protobuf SDK によって逆シリアル化できます。 テキスト ベースのコンテンツのみをサポートするクライアント (例: json.webpubsub.azure.v1) は、Base64 でエンコードされたバイナリを受け取ります。

  • text: send_to_group_message.data.text_data を設定した場合、暗黙的な dataTypetext です。 text_data は文字列である必要があります。 他のプロトコルを使用するすべてのクライアントは、UTF-8 でエンコードされた文字列を受け取ります。

  • binary: send_to_group_message.data.binary_data を設定した場合、暗黙的な dataTypebinary です。 binary_data はバイト配列である必要があります。 他のプロトコルを使用するすべてのクライアントは、protobuf エンコードされていない未加工のバイナリを受け取ります。 テキスト ベースのコンテンツのみをサポートするクライアント (例: json.webpubsub.azure.v1) は、Base64 でエンコードされたバイナリを受け取ります。

ケース 1: テキスト データを発行する

send_to_group_message.groupgroup に設定し、send_to_group_message.data.text_data"text data" に設定します。

  • グループ group 内の protobuf サブプロトコル クライアントはバイナリ フレームを受け取り、DownstreamMessage を使用して逆シリアル化できます。

  • group の JSON サブプロトコル クライアントは、以下を受け取ります。

    {
        "type": "message",
        "from": "group",
        "group": "group",
        "dataType" : "text",
        "data" : "text data"
    }
    
  • group のシンプル WebSocket クライアントは、文字列 text data を受け取ります。

ケース 2: protobuf データを発行する

カスタム メッセージがある場合を想定します。

message MyMessage {
    int32 value = 1;
}

send_to_group_message.groupgroup に、send_to_group_message.data.protobuf_dataAny.pack(MyMessage) に設定し、value = 1 を指定します。

  • group 内の protobuf サブプロトコル クライアントはバイナリ フレームを受け取り、DownstreamMessage を使用して逆シリアル化できます。

  • group 内のサブプロトコル クライアントは以下を受け取ります。

    {
        "type": "message",
        "from": "group",
        "group": "G",
        "dataType" : "protobuf",
        "data" : "Ci90eXBlLmdvb2dsZWFwaXMuY29tL2F6dXJlLndlYnB1YnN1Yi5UZXN0TWVzc2FnZRICCAE=" // Base64-encoded bytes
    }
    

    Note

    データは、Base64 でエンコードされた逆シリアル化可能な protobuf バイナリです。

次の protobuf 定義を使用し、Any.unpack() を使用して逆シリアル化できます。

syntax = "proto3";

message MyMessage {
    int32 value = 1;
}
  • group のシンプル WebSocket クライアントは、バイナリー データを受け取ります。

    # Show in hexadecimal
    0A 2F 74 79 70 65 2E 67 6F 6F 67 6C 65 61 70 69 73 2E 63 6F 6D 2F 61 7A 75 72 65 2E 77 65 62 70 75 62 73 75 62 2E 54 65 73 74 4D 65 73 73 61 67 65 12 02 08 01
    

ケース 3: バイナリ データを発行する

send_to_group_message.groupgroup に設定し、send_to_group_message.data.binary_data[1, 2, 3] に設定します。

  • グループ group 内の protobuf サブプロトコル クライアントはバイナリ フレームを受け取り、DownstreamMessage を使用して逆シリアル化できます。

  • グループ group 内の JSON サブプロトコル クライアントは以下を受け取ります。

    {
        "type": "message",
        "from": "group",
        "group": "group",
        "dataType" : "binary",
        "data" : "AQID", // Base64-encoded [1,2,3]
    }
    

    JSON サブプロトコル クライアントはテキスト ベースのメッセージングのみをサポートします。バイナリは常に Base64 でエンコードされます。

  • group のシンプル WebSocket クライアントは、次のバイナリ フレームのバイナリ データを受け取ります。

    # Show in hexadecimal
    01 02 03
    

メッセージのストリーミングを始めましょう

グループストリームを開始するには、 send_to_group_message.group をターゲットグループに設定し、 send_to_group_message.streamStreamStartInfo メッセージに設定します。 ストリームスタートリクエストは send_to_group_message.datasend_to_group_message.ack_idを設定しません。

  • send_to_group_message.stream.stream_id は論理ストリームの識別子です。 文字列は空でなければならず、同じクライアント接続上のアクティブなストリーム間で一意でなければなりません。 クライアントライブラリは、GUIDやUUIDなどのグローバルな一意値を生成することが推奨されています。
  • send_to_group_message.stream.idle_timeout_ms はオプションです。 もし指定されている場合は、 0より大きくなければなりません。 省略すると、サービスのデフォルトは 300000 ミリ秒となります。 値としてはアイドルタイムアウトであって、ストリームの総寿命ではありません。 ストリームデータを送信するか、ストリームキーパライブを送信するか、このタイムアウトが経過する前にストリームを終了するか、アプリケーションがストリームを開いたままにしておく必要があります。
  • send_to_group_message.no_echo はオプションです。 trueに設定すると、ストリームメッセージは同じ接続にエコーバックされません。 設定しない場合の既定値は false です。

ストリームが受け入れられると、クライアントはexpected_sequence_id1に設定されたストリームack応答を受け取ります。

ストリーミングデータを送信する

ストリームデータを送るには、 stream_data_message.stream_idstream_data_message.stream_sequence_idstream_data_message.dataを設定します。

  • stream_data_message.stream_id 同じクライアント接続上のアクティブなストリームを識別します。
  • stream_data_message.stream_sequence_id は正のuint64数です。 ストリームの最初のデータ断片は 1を使用し、同じ stream_id の後続のデータ断片は正確に 1増加します。
  • stream_data_message.data Publish Messageと同じエンコーディングルールMessageDataを使用します。

データを加入者に届けずにストリームをアクティブに保つには、stream_id設定のまま送信stream_data_message

ストリーミングメッセージ終了

配信を終了するには、 stream_end_message.stream_idを設定します。

アプリケーション定義エラーでストリームを終了するには、 stream_end_message.errorを設定します。

  • stream_end_message.error.message は任意の人間が読み取れるエラーメッセージです。
  • stream_end_message.error.user_error_code はオプションのアプリケーション定義エラーコードです。

ストリームが閉鎖されると、パブリッシャーは ストリームクローズドレスポンスを受け取ります。

カスタム イベントを送信する

暗黙的なdataType があり、これは設定した protobuf に基づいて、textbinary、または dataType のいずれかにすることができます。 受信側クライアントは dataType を利用して、コンテンツを正しく処理できます。

  • protobuf: event_message.data.protobuf_data を設定した場合、暗黙的な dataTypeprotobuf です。 protobuf_data の値は、サポートされている任意の protobuf 型を指定できます。 イベント ハンドラーは protobuf でエンコードされたバイナリを受け取り、任意の protobuf SDK によって逆シリアル化できます。

  • text: event_message.data.text_data を設定した場合、暗黙的な dataTypetext です。 text_data の値は、文字列である必要があります。 イベント ハンドラーは、UTF-8 でエンコードされた文字列を受け取ります。

  • binary: event_message.data.binary_data を設定した場合、暗黙的な dataTypebinary です。 binary_data の値はバイト配列である必要があります。 イベント ハンドラーは、生のバイナリ フレームを受け取ります。

ケース 1: テキスト データを含むイベントを送信する

event_message.data.text_data"text data" に設定します。

アップストリーム イベント ハンドラーは、次に似た要求を受け取ります。

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: text/plain
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.user.<event_name>
ce-source: /client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub_name}
ce-eventName: <event_name>

text data

Content-Type text/plain dataType の場合、CloudEvents HTTP 要求の = は textになります。

ケース 2: protobuf データを含むイベントを送信する

次の顧客メッセージを受信したとします。

message MyMessage {
    int32 value = 1;
}

event_message.data.protobuf_dataany.pack(MyMessage) に設定し、value = 1 を指定します。

アップストリーム イベント ハンドラーは、次に似た要求を受け取ります。

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: application/json
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.user.<event_name>
ce-source: /client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub_name}
ce-eventName: <event_name>

// Just show in hexadecimal; read it as binary
0A 2F 74 79 70 65 2E 67 6F 6F 67 6C 65 61 70 69 73 2E 63 6F 6D 2F 61 7A 75 72 65 2E 77 65 62 70 75 62 73 75 62 2E 54 65 73 74 4D 65 73 73 61 67 65 12 02 08 01

Content-Type application/x-protobuf dataType の場合、CloudEvents HTTP 要求の = は protobufになります。

データは有効な protobuf バイナリです。 次の proto および any.unpack() を使用して、逆シリアル化できます。

syntax = "proto3";

message MyMessage {
    int32 value = 1;
}

ケース 3: バイナリ データを含むイベントを送信する

send_to_group_message.binary_data[1, 2, 3] に設定します。

アップストリーム イベント ハンドラーは、次に似た要求を受け取ります。

POST /upstream HTTP/1.1
Host: xxxxxx
WebHook-Request-Origin: xxx.webpubsub.azure.com
Content-Type: application/octet-stream
Content-Length: nnnn
ce-specversion: 1.0
ce-type: azure.webpubsub.user.<event_name>
ce-source: /client/{connectionId}
ce-id: {eventId}
ce-time: 2021-01-01T00:00:00Z
ce-signature: sha256={connection-id-hash-primary},sha256={connection-id-hash-secondary}
ce-userId: {userId}
ce-connectionId: {connectionId}
ce-hub: {hub_name}
ce-eventName: <event_name>

// Just show in hexadecimal; you need to read it as binary
01 02 03 

dataType = binary の場合、CloudEvents HTTP 要求の Content-Typeapplication/octet-stream です。 WebSocket フレームは、テキスト メッセージ フレームの場合の text 形式であるか、binary メッセージ フレームの場合の UTF8 でエンコードされたバイナリです。

メッセージが、記述された形式と一致していない場合、サービスによってクライアントが拒否されます。

ping

クライアントはサービスに PingMessage を送信して、Web PubSub サービスがクライアントの稼働を検出できるようにすることができます。

応答

すべての応答メッセージは、次の protobuf 形式に従います。

message DownstreamMessage {
    oneof message {
        AckMessage ack_message = 1;
        DataMessage data_message = 2;
        SystemMessage system_message = 3;
        PongMessage pong_message = 4;
        StreamAckMessage stream_ack_message = 6;
        StreamNackMessage stream_nack_message = 7;
        StreamClosedMessage stream_closed_message = 8;
    }
    
    message AckMessage {
        uint64 ack_id = 1;
        bool success = 2;
        optional ErrorMessage error = 3;
    
        message ErrorMessage {
            string name = 1;
            string message = 2;
        }
    }

    message DataMessage {
        string from = 1;
        optional string group = 2;
        MessageData data = 3;
        StreamInfo stream = 6;
    }

    message SystemMessage {
        oneof message {
            ConnectedMessage connected_message = 1;
            DisconnectedMessage disconnected_message = 2;
        }
    
        message ConnectedMessage {
            string connection_id = 1;
            string user_id = 2;
        }

        message DisconnectedMessage {
            string reason = 2;
        }
    }

    message PongMessage {
    }

    message StreamAckMessage {
        string stream_id = 1;
        uint64 expected_sequence_id = 2;
    }

    message StreamNackMessage {
        string stream_id = 1;
        string name = 2;
        string message = 3;
        uint64 expected_sequence_id = 4;
    }

    message StreamClosedMessage {
        string stream_id = 1;
        optional StreamClosedError error = 2;

        message StreamClosedError {
            string name = 1;
            string message = 2;
        }
    }
}

message StreamInfo {
    string stream_id = 1;
    uint64 stream_sequence_id = 2;
    optional bool end_of_stream = 3;
    optional StreamError error = 4;

    message StreamError {
        string name = 1;
        string message = 2;
        string user_error_code = 3;
    }
}

クライアントが受信するメッセージは、 ackmessagesystempongstreamAckstreamNack、または streamClosedのままです。

確認応答

要求に ackId が含まれている場合、サービスからは、この要求に対する確認応答が返されます。 クライアント実装では、次を含むこの ACK メカニズムを処理する必要があります。

  • async await 操作の ACK 応答を待機する。
  • 一定の期間中に ACK 応答が受信されていない場合にタイムアウト チェックを行う。

クライアント実装では必ず、success の状態が true または false のどちらであるかを最初に確認する必要があります。 success の状態が false の場合、クライアントは error プロパティからエラーの詳細を読み取れます。

メッセージ応答

クライアントは、クライアントが参加しているグループから発行されたメッセージを受信できます。 または、サーバーが特定のクライアントまたは特定のユーザーにメッセージを送信するときに、サーバー管理ロールからメッセージを受信できます。

次のシナリオでは、常に DownstreamMessage.DataMessage メッセージが表示されます。

  • メッセージがグループからのものである場合、fromgroup です。 メッセージがサーバーからのものである場合、fromserver です。
  • メッセージがグループからのものである場合、group はグループ名です。

送信者の dataType は、次のいずれかのメッセージを送信します。

  • dataTypetext の場合、message_response_message.data.text_data を使用します。
  • dataTypebinary の場合、message_response_message.data.binary_data を使用します。
  • dataTypeprotobuf の場合、message_response_message.data.protobuf_data を使用します。
  • dataTypejson の場合、message_response_message.data.text_data を使用します。また、コンテンツはシリアル化された JSON 文字列です。

グループメッセージがストリームに属している場合、 DownstreamMessage.DataMessage.stream が設定されます。

  • stream.stream_id は論理ストリーム識別子です。
  • stream.stream_sequence_id はストリーム内のメッセージのシーケンス番号です。
  • stream.end_of_stream はオプションです。 trueに設定すると、メッセージはストリームの終端メッセージとなります。
  • stream.error は任意であり、ストリームがエラーで終了した場合にのみ存在します。 user_error_code UserErrorのみ存在します。

ストリームackレスポンス

サービスは DownstreamMessage.StreamAckMessage を送り、受け入れられたストリームデータを確認し、次のストリームシーケンスIDを報告します。

  • stream_id は論理ストリーム識別子です。
  • expected_sequence_id はサービスが期待する次のストリームシーケンスIDです。

ストリームナックレスポンス

サービスはリトイブルストリームエラーのために DownstreamMessage.StreamNackMessage を送信します。

  • stream_id は論理ストリーム識別子です。
  • expected_sequence_id はサービスが期待する次のストリームシーケンスIDです。
  • name には、InvalidSequenceId または TransientError を指定できます。
  • message エラーの詳細を含む。

ストリームクローズドレスポンス

パブリッシャー側のストリームが閉じられたときにサービスは送信 DownstreamMessage.StreamClosedMessage します。

  • stream_id は論理ストリーム識別子です。
  • error ストリームが通常閉鎖されている場合は省略されます。
  • error.name StreamNotFoundForbiddenBadRequestInternalServerError、またはIdleTimeoutがあります。
  • error.message エラーの詳細を含む。

システム応答

Web PubSub サービスでは、システム関連の応答をクライアントに送信することもできます。

接続済み

クライアントがサービスに接続すると、DownstreamMessage.SystemMessage.ConnectedMessage メッセージが表示されます。

Disconnected

サーバーによって接続が閉じられるとき、またはサービスによってクライアントが拒否されるとき、DownstreamMessage.SystemMessage.DisconnectedMessage メッセージを受け取ります。

Pong 応答

Web PubSub サービスは、クライアントから PongMessage を受信すると、PingMessage をクライアントに送信します。

次のステップ

これらのリソースを使用して、独自のアプリケーションの構築を開始します。