O subprotocolo WebSocket protobuf com suporte no Azure Web PubSub
Este documento descreve o subprotocolo protobuf.webpubsub.azure.v1
.
Quando um cliente está usando esse subprotocolo, os quadros de dados de saída e de entrada são esperados como conteúdo de buffer de protocolo (protobuf).
Visão geral
O subprotocolo protobuf.webpubsub.azure.v1
capacita o cliente a fazer uma publicação/assinatura (PubSub) diretamente, em vez de uma viagem de ida e volta para o servidor upstream. Chamamos a conexão WebSocket com o subprotocolo protobuf.webpubsub.azure.v1
de cliente WebSocket PubSub.
Por exemplo, no JavaScript, você pode criar um cliente WebSocket PubSub com o subprotocolo protobuf usando:
// PubSub WebSocket client
var pubsub = new WebSocket('wss://test.webpubsub.azure.com/client/hubs/hub1', 'protobuf.webpubsub.azure.v1');
Para um cliente WebSocket simples, o servidor tem a função necessária de lidar com eventos de clientes. Uma conexão WebSocket simples sempre dispara um evento message
quando envia mensagens e sempre se baseia no lado do servidor para processar mensagens e realizar outras operações. Com a ajuda do subprotocolo protobuf.webpubsub.azure.v1
, um cliente autorizado pode ingressar em um grupo usando solicitações de ingresso e publicar mensagens em um grupo usando publicar solicitações diretamente. O cliente também pode rotear mensagens para diversos manipuladores de eventos de upstream usando as solicitações de eventos a fim de personalizar o evento ao qual a mensagem pertence.
Observação
Atualmente, o serviço Web PubSub dá suporte apenas a proto3.
Permissões
Um cliente WebSocket PubSub só pode publicar em outros clientes quando estiver autorizado. O atribuído roles
ao cliente determina as permissões concedidas ao cliente:
Função | Permissão |
---|---|
Não especificado | O cliente pode enviar solicitações de evento. |
webpubsub.joinLeaveGroup |
O cliente pode ingressar/sair de qualquer grupo. |
webpubsub.sendToGroup |
O cliente pode publicar mensagens em qualquer grupo. |
webpubsub.joinLeaveGroup.<group> |
O cliente pode ingressar/sair do grupo <group> . |
webpubsub.sendToGroup.<group> |
O cliente pode publicar mensagens no grupo <group> . |
O servidor pode conceder ou revogar dinamicamente as permissões do cliente pelas APIs REST ou SDKs de servidor.
Requests
Todas as mensagens de solicitação seguem este formato de 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;
}
message SendToGroupMessage {
string group = 1;
optional uint64 ack_id = 2;
MessageData data = 3;
}
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 MessageData {
oneof data {
string text_data = 1;
bytes binary_data = 2;
google.protobuf.Any protobuf_data = 3;
}
}
Ingressar grupos
Formato:
Defina join_group_message.group
como o nome do grupo.
ackId
é a identidade de cada solicitação e deve ser exclusiva. O serviço envia uma mensagem de resposta ack para notificar o resultado do processo da solicitação. Mais detalhes podem ser encontrados em Resposta AckId e Ack
Sair dos grupos
Formato:
Defina leave_group_message.group
como o nome do grupo.
ackId
é a identidade de cada solicitação e deve ser exclusiva. O serviço envia uma mensagem de resposta ack para notificar o resultado do processo da solicitação. Mais detalhes podem ser encontrados em Resposta AckId e Ack
Publicar mensagens
Formato:
ackId
: a identidade exclusiva de cada solicitação. O serviço envia uma mensagem de resposta ack para notificar o resultado do processo da solicitação. Mais detalhes podem ser encontrados em Resposta AckId e AckdataType
: o formato de dados, que pode serprotobuf
,text
oubinary
dependendo dodata
emMessageData
. Os clientes receptores podem usardataType
para processarem o conteúdo corretamente.protobuf
: quando você definesend_to_group_message.data.protobuf_data
, o implícitodataType
éprotobuf
.protobuf_data
pode ser do tipo de mensagem Qualquer. Todos os outros clientes receberão um binário codificado em protobuf que pode ser desserializado pelo SDK do protobuf. Os clientes que dão suporte apenas ao conteúdo baseado em texto (por exemplo,json.webpubsub.azure.v1
) recebem um binário codificado em Base64.text
: quando você definesend_to_group_message.data.text_data
, o implícitodataType
étext
.text_data
deve ser uma cadeia de caracteres. Todos os clientes com outros protocolos receberão uma cadeia de caracteres codificada em UTF-8.binary
: quando você definesend_to_group_message.data.binary_data
, o implícitodataType
ébinary
.binary_data
deve ser uma matriz de byte. Todos os clientes com outros protocolos receberão um binário bruto sem codificação de protobuf. Os clientes que dão suporte apenas ao conteúdo baseado em texto (por exemplo,json.webpubsub.azure.v1
) recebem um binário codificado em Base64.
Caso 1: publicar dados de texto
Defina send_to_group_message.group
como group
e send_to_group_message.data.text_data
como "text data"
.
O cliente do subprotocolo protobuf do grupo
group
recebe o quadro binário e pode usar DownstreamMessage para desserializá-lo.Os clientes do subprotocolo JSON em
group
recebem:{ "type": "message", "from": "group", "group": "group", "dataType" : "text", "data" : "text data" }
Os clientes WebSocket simples em
group
recebemtext data
da cadeia de caracteres.
Caso 2: publicar dados de protobuf
Digamos que você tenha uma mensagem personalizada:
message MyMessage {
int32 value = 1;
}
Defina send_to_group_message.group
como group
e send_to_group_message.data.protobuf_data
como Any.pack(MyMessage)
com value = 1
.
Os clientes do subprotocolo protobuf em
group
recebem o quadro binário e podem usar DownstreamMessage para desserializá-lo.O cliente de subprotocolo em
group
recebe:{ "type": "message", "from": "group", "group": "G", "dataType" : "protobuf", "data" : "Ci90eXBlLmdvb2dsZWFwaXMuY29tL2F6dXJlLndlYnB1YnN1Yi5UZXN0TWVzc2FnZRICCAE=" // Base64-encoded bytes }
Observação
Os dados são um binário protobuf desserializável que foi codificado em Base64.
Você pode usar a seguinte definição de protobuf e Any.unpack()
para desserializar:
syntax = "proto3";
message MyMessage {
int32 value = 1;
}
Os clientes WebSocket simples em
group
recebem o quadro binário:# 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
Caso 3: publicar dados binários
Defina send_to_group_message.group
como group
e send_to_group_message.data.binary_data
como [1, 2, 3]
.
O cliente do subprotocolo protobuf do grupo
group
recebe o quadro binário e pode usar DownstreamMessage para desserializá-lo.O cliente do subprotocolo JSON do grupo
group
recebe:{ "type": "message", "from": "group", "group": "group", "dataType" : "binary", "data" : "AQID", // Base64-encoded [1,2,3] }
Como o cliente do subprotocolo JSON dá suporte apenas a mensagens baseadas em texto, o binário é sempre codificado em Base64.
Os clientes WebSocket simples em
group
recebem os dados binários no quadro binário:# Show in hexadecimal 01 02 03
Enviar eventos personalizados
Há um dataType
implícito que pode ser protobuf
, text
ou binary
, dependendo do dataType
definido. Os clientes receptores podem utilizar dataType
para lidar com o conteúdo corretamente.
protobuf
: quando você defineevent_message.data.protobuf_data
, o implícitodataType
éprotobuf
. O valorprotobuf_data
pode ser qualquer tipo de protobuf com suporte. O manipulador de eventos recebe um binário codificado em protobuf que pode ser desserializado por qualquer SDK do protobuf.text
: quando você defineevent_message.data.text_data
, o implícitodataType
étext
. O valortext_data
deve ser uma cadeia de caracteres. O manipulador de eventos recebe uma cadeia de caracteres codificada em UTF-8.binary
: quando você defineevent_message.data.binary_data
, o implícitodataType
ébinary
. O valorbinary_data
deve ser uma matriz de bytes. O manipulador de eventos recebe o quadro binário bruto.
Caso 1: enviar um evento com os dados de texto
Definir event_message.data.text_data
para "text data"
.
O manipulador de eventos upstream recebe uma solicitação semelhante a:
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
O Content-Type
da solicitação HTTP do CloudEvents é text/plain
, em que dataType
=text
.
Caso 2: enviar um evento com os dados de protobuf
Suponha que você recebeu a seguinte mensagem de cliente:
message MyMessage {
int32 value = 1;
}
Definir event_message.data.protobuf_data
como any.pack(MyMessage)
com value = 1
O manipulador de eventos upstream recebe uma solicitação semelhante a:
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
O Content-Type
da solicitação HTTP do CloudEvents é application/x-protobuf
, em que dataType
=protobuf
.
Os dados são um binário de protobuf válido. Você pode usar o proto
e o any.unpack()
a seguir para desserializá-lo:
syntax = "proto3";
message MyMessage {
int32 value = 1;
}
Caso 3: enviar evento com os dados binários
Definir send_to_group_message.binary_data
para [1, 2, 3]
.
O manipulador de eventos upstream recebe uma solicitação semelhante a:
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
Para dataType
=binary
, o Content-Type
para a solicitação HTTP CloudEvents é application/octet-stream
. O quadro WebSocket pode estar no formato text
para os quadros de mensagem de texto ou binários codificados em UTF-8 para quadros de mensagem binary
.
O serviço recusará o cliente se a mensagem não corresponder ao formato prescrito.
Respostas
Todas as mensagens de resposta seguem este formato de protobuf:
message DownstreamMessage {
oneof message {
AckMessage ack_message = 1;
DataMessage data_message = 2;
SystemMessage system_message = 3;
}
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;
}
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;
}
}
}
As mensagens recebidas pelo cliente podem ser de um destes três tipos: ack
, message
ou system
.
Resposta Ack
Se a solicitação contiver ackId
, o serviço retornará uma resposta ack para essa solicitação. A implementação do cliente deve lidar com esse mecanismo ack, incluindo:
- Aguardar a resposta ack para uma operação
async
await
. - Fazer uma verificação de tempo limite quando a resposta ack não é recebida durante determinado período.
A implementação do cliente sempre deve verificar primeiro se o status success
é true
ou false
. Quando o status success
é false
, o cliente pode ler da propriedade error
para obter detalhes de erro.
Resposta da mensagem
Os clientes podem receber mensagens publicadas de um grupo em que o cliente ingressou. Ou podem receber mensagens da função de gerenciamento do servidor quando o servidor envia mensagens para um cliente ou usuário específico.
Você sempre obterá uma mensagem DownstreamMessage.DataMessage
nos seguintes cenários:
- Quando a mensagem for de um grupo,
from
serágroup
. Quando a mensagem for do servidor,from
seráserver
. - Quando a mensagem for de um grupo,
group
será o nome do grupo.
O dataType
do remetente fará com que uma das seguintes mensagens seja enviada:
- Se
dataType
fortext
, usemessage_response_message.data.text_data
. - Se
dataType
forbinary
, usemessage_response_message.data.binary_data
. - Se
dataType
forprotobuf
, usemessage_response_message.data.protobuf_data
. - Se
dataType
forjson
, usemessage_response_message.data.text_data
, e o conteúdo será a cadeia de caracteres JSON serializada.
Resposta do sistema
O serviço Web PubSub também pode enviar respostas relacionadas ao sistema para o cliente.
Conectado
Quando o cliente se conecta ao serviço, você recebe uma mensagem DownstreamMessage.SystemMessage.ConnectedMessage
.
Desconectado
Quando o servidor encerra a conexão ou o serviço recusa o cliente, você recebe uma mensagem DownstreamMessage.SystemMessage.DisconnectedMessage
.
Próximas etapas
Use estes recursos para começar a criar seu aplicativo: