Subprotocolo WebSocket JSON confiável do Azure Web PubSub

O subprotocolo WebSocket JSON json.reliable.webpubsub.azure.v1 permite a troca altamente confiável de mensagens de publicação/assinatura diretamente entre clientes por meio do serviço sem uma viagem de ida e volta para o servidor upstream.

Este documento descreve o subprotocolo json.reliable.webpubsub.azure.v1.

Quando as conexões de cliente WebSocket são descartadas devido a problemas de rede intermitentes, as mensagens podem ser perdidas. Em um sistema pub/sub, os editores são dissociados dos assinantes e podem não detectar a perda de conexão ou mensagem de um assinante.

Para superar problemas de rede intermitentes e manter a entrega de mensagens confiável, você pode usar o subprotocolo json.reliable.webpubsub.azure.v1 do Azure WebPubSub para criar um cliente WebSocket PubSub confiável.

Um cliente WebSocket PubSub confiável pode:

Por exemplo, você pode criar um cliente WebSocket PubSub confiável com o seguinte código JavaScript:

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

Veja Como criar clientes confiáveis para implementar a reconexão e a confiabilidade de mensagens para clientes editor e assinante.

Quando o cliente está usando esse subprotocolo, os quadros de dados de saída e de entrada precisam conter conteúdos JSON.

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>.
webpubsub.joinLeaveGroups.<pattern> O cliente pode ingressar/sair de qualquer grupo cujo nome corresponde <pattern> (consulte padrões de função de grupo curinga).
webpubsub.sendToGroups.<pattern> O cliente pode publicar mensagens em qualquer grupo cujo nome corresponde <pattern> (consulte padrões de função de grupo curinga).

O servidor pode conceder ou revogar dinamicamente as permissões do cliente pelas APIs REST ou SDKs de servidor.

Observação

Funções curinga (por exemplo, webpubsub.sendToGroups.<pattern>) ainda não têm suporte em APIs REST ou SDKs de servidor durante o runtime.

Requests

Ingressar grupos

Formato:

{
    "type": "joinGroup",
    "group": "<group_name>",
    "ackId" : 1
}
  • 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. Para obter detalhes, consulte AckId e Ack Response

Sair dos grupos

Formato:

{
    "type": "leaveGroup",
    "group": "<group_name>",
    "ackId" : 1
}
  • 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. Para obter detalhes, consulte AckId e Ack Response

Publicar mensagens

Formato:

{
    "type": "sendToGroup",
    "group": "<group_name>",
    "ackId" : 1,
    "noEcho": true|false,
    "dataType" : "json|text|binary",
    "data": {}, // data can be string or valid json token depending on the dataType 
}
  • 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. Para obter detalhes, consulte AckId e Ack Response
  • noEcho é opcional. Se definida como verdadeira, essa mensagem não será ecoada de volta para a mesma conexão. Se não for definida, o valor padrão será falso.
  • dataType pode ser definido como json, text ou binary:
    • json: data pode ser qualquer tipo que o JSON dá suporte e será publicado como ele é. Se dataType não for especificado, o padrão será json.
    • text: data deve estar no formato de cadeia de caracteres e os dados da cadeia de caracteres serão publicados;
    • binary: data deve estar no formato base64 e os dados binários serão publicados;

Caso 1: publicar dados de texto:

{
    "type": "sendToGroup",
    "group": "<group_name>",
    "dataType" : "text",
    "data": "text data",
    "ackId": 1
}
  • Os clientes do subprotocolo em <group_name> recebem:
{
    "type": "message",
    "from": "group",
    "group": "<group_name>",
    "dataType" : "text",
    "data" : "text data"
}
  • Os clientes WebSocket simples em <group_name> recebem a cadeia de caracteres text data.

Caso 2: publicar dados JSON:

{
    "type": "sendToGroup",
    "group": "<group_name>",
    "dataType" : "json",
    "data": {
        "hello": "world"
    }
}
  • Os clientes do subprotocolo em <group_name> recebem:
{
    "type": "message",
    "from": "group",
    "group": "<group_name>",
    "dataType" : "json",
    "data" : {
        "hello": "world"
    }
}
  • Os clientes WebSocket simples em <group_name> recebem a cadeia de caracteres serializada {"hello": "world"}.

Caso 3: publicar dados binários:

{
    "type": "sendToGroup",
    "group": "<group_name>",
    "dataType" : "binary",
    "data": "<base64_binary>",
    "ackId": 1
}
  • Os clientes do subprotocolo em <group_name> recebem:
{
    "type": "message",
    "from": "group",
    "group": "<group_name>",
    "dataType" : "binary",
    "data" : "<base64_binary>", 
}
  • Os clientes WebSocket simples em <group_name> recebem os dados binários no quadro binário.

Iniciar a transmissão de mensagens

Para iniciar uma transmissão em grupo, envie um sendToGroup pedido com a stream propriedade. Uma requisição de início de fluxo não contém data, dataType, ou ackId.

Formato:

{
    "type": "sendToGroup",
    "group": "<group_name>",
    "noEcho": true|false,
    "stream": {
        "streamId": "<stream_id>",
        "idleTimeoutMs": 300000
    }
}
  • stream.streamId é o identificador do fluxo lógico. Deve ser uma string não vazia e deve ser única entre os fluxos ativos na mesma conexão cliente. Bibliotecas clientes são recomendadas para gerar um valor globalmente único, como um GUID ou UUID.
  • stream.idleTimeoutMs é opcional. Se especificado, deve ser maior que 0. Se omitido, o padrão do serviço é 300000 milissegundos. O valor é um tempo de espera ocioso, não uma vida útil total do fluxo. Envie dados do fluxo, envie um keep epalive ou encerre o fluxo antes que esse timeout expire, quando o aplicativo precisa manter o stream aberto.
  • noEcho é opcional. Se configurado como true, as mensagens do stream não são retornadas para a mesma conexão. Se não for definida, o valor padrão será falso.

Quando o fluxo é aceito, o cliente recebe uma resposta de ack do fluxo com expectedSequenceId definido para 1.

Enviar dados em streaming

Para enviar dados de fluxo, envie uma streamData solicitação com streamId, streamSequenceId, dataType, e data.

Formato:

{
    "type": "streamData",
    "streamId": "<stream_id>",
    "streamSequenceId": 1,
    "dataType" : "json|text|binary",
    "data": {}
}
  • streamId identifica um fluxo ativo na mesma conexão do cliente.
  • streamSequenceId é um número uint64 positivo. O primeiro fragmento de dados em um fluxo usa 1, e cada fragmento de dado seguinte para o mesmo streamId aumenta exatamente 1em .
  • dataType pode ser definido como json, text, ou binary, com as mesmas regras de codificação de dados que publish messages.

Para manter um fluxo ativo sem entregar dados aos assinantes, envie uma streamData solicitação apenas type com e streamId.

{
    "type": "streamData",
    "streamId": "<stream_id>"
}

Encerrar mensagens de streaming

Para encerrar uma transmissão, envie um streamEnd pedido.

Formato:

{
    "type": "streamEnd",
    "streamId": "<stream_id>"
}

Para encerrar um fluxo com um erro definido pela aplicação, inclua a propriedade opcional error .

{
    "type": "streamEnd",
    "streamId": "<stream_id>",
    "error": {
        "message": "<error_detail>",
        "userErrorCode": "<application_error_code>"
    }
}
  • error.message é uma mensagem de erro opcional legível por humanos.
  • error.userErrorCode é um código de erro opcional definido pela aplicação.

Quando o fluxo é fechado, o editor recebe uma resposta de stream fechado.

Enviar eventos personalizados

Formato:

{
    "type": "event",
    "event": "<event_name>",
    "ackId": 1,
    "dataType" : "json|text|binary",
    "data": {}, // data can be string or valid json token depending on the dataType 
}
  • 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. Para obter detalhes, consulte AckId e Ack Response

dataType pode ser um de text, binary ou json:

  • json: os dados podem ser qualquer tipo para o qual o JSON dá suporte e serão publicados como eles são. O padrão é json.
  • text: os dados estão no formato de cadeia de caracteres e os dados da cadeia de caracteres serão publicados;
  • binary: os dados devem estar no formato base64 e os dados binários serão publicados;

Caso 1: enviar evento com dados de texto:

{
    "type": "event",
    "event": "<event_name>",
    "ackId": 1,
    "dataType" : "text",
    "data": "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 evento com dados JSON:

{
    "type": "event",
    "event": "<event_name>",
    "ackId": 1,
    "dataType" : "json",
    "data": {
        "hello": "world"
    }, 
}

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>

{
    "hello": "world"
}

O Content-Type da solicitação HTTP do CloudEvents é application/json, em que dataType é json

Caso 3: enviar evento com dados binários:

{
    "type": "event",
    "event": "<event_name>",
    "ackId": 1,
    "dataType" : "binary",
    "data": "base64_binary", 
}

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>

binary

O Content-Type da solicitação HTTP do CloudEvents é application/octet-stream, em que dataType é binary. O quadro WebSocket pode ser o formato text para os quadros de mensagem de texto ou binários codificados UTF8 para quadros de mensagem binary.

O serviço Web PubSub recusará o cliente se a mensagem não corresponder ao formato descrito.

Ping

Formato:

{
    "type": "ping",
}

O cliente pode enviar uma mensagem de ping para o serviço para habilitar o serviço Web PubSub a detectar a atividade do cliente.

Confirmação de sequência

Formato:

{
    "type": "sequenceAck",
    "sequenceId": "<sequenceId>",
}

O cliente WebSocket PubSub confiável precisará enviar uma mensagem de confirmação de sequência assim que receber uma mensagem do serviço. Para obter mais informações, confira Como criar clientes confiáveis

  • sequenceId é um número uint64 incremental da mensagem recebida.

Respostas

As mensagens recebidas pelo cliente podem ser de vários tipos: ack, message, system, pong, streamAck, streamNack, e streamClosed. As mensagens com o tipo message têm a propriedade sequenceId. O cliente precisa enviar a Confirmação de sequência ao serviço depois de receber uma mensagem.

Resposta Ack

Quando a solicitação contiver ackId, o serviço retornará uma resposta rápida para essa solicitação. A implementação do cliente deve lidar com esse mecanismo de confirmação, incluindo aguardar a resposta de confirmação para uma operação asyncawait e ter uma verificação de tempo limite quando a resposta de confirmação não for recebida durante um determinado período.

Formato:

{
    "type": "ack",
    "ackId": 1, // The ack id for the request to ack
    "success": false, // true or false
    "error": {
        "name": "Forbidden|InternalServerError|Duplicate",
        "message": "<error_detail>"
    }
}

A implementação do cliente DEVERÁ sempre primeiro verificar se o success é true ou false. Somente quando success for false, o cliente lê a partir de error.

Resposta da mensagem

Os clientes podem receber mensagens publicadas de um grupo que o cliente ingressou ou do servidor, que, operando em uma função de gerenciamento de servidor, envia mensagens para clientes ou usuários específicos.

  1. A mensagem de resposta de um grupo:

    {
        "sequenceId": 1,
        "type": "message",
        "from": "group",
        "group": "<group_name>",
        "dataType": "json|text|binary",
        "data" : {} // The data format is based on the dataType
        "fromUserId": "abc"
    }
    
  2. A mensagem de resposta do servidor:

    {
        "sequenceId": 1,
        "type": "message",
        "from": "server",
        "dataType": "json|text|binary",
        "data" : {} // The data format is based on the dataType
    }
    

Casa 1: enviar dados Olá, Mundo para conexão por meio da API REST com Content-Type=text/plain

  • Um cliente WebSocket simples recebe um quadro WebSocket de texto com os dados: Olá, Mundo;

  • Um cliente WebSocket PubSub recebe a mensagem em JSON:

    {
        "sequenceId": 1,
        "type": "message",
        "from": "server",
        "dataType" : "text",
        "data": "Hello World", 
    }
    

Caso 2: enviar dados { "Hello" : "World"} para a conexão API REST com Content-Type=application/json

  • Um cliente WebSocket simples recebe um quadro WebSocket de texto com os dados de cadeia de caracteres: { "Hello" : "World"};

  • Um cliente WebSocket PubSub recebe a mensagem em JSON:

    {
        "sequenceId": 1,
        "type": "message",
        "from": "server",
        "dataType" : "json",
        "data": {
            "Hello": "World"
        }
    }
    

Se a API REST estiver enviando uma cadeia de caracteres Olá, Mundo usando o tipo de conteúdo application/json, o cliente WebSocket simples receberá a cadeia de caracteres JSON "Olá, Mundo" encapsulada em ".

Caso 3: enviar dados binários para a conexão por meio da API REST com Content-Type=application/octet-stream

  • Um cliente WebSocket simples recebe um quadro WebSocket binário com os dados binários.

  • Um cliente WebSocket PubSub recebe a mensagem em JSON:

    {
        "sequenceId": 1,
        "type": "message",
        "from": "server",
        "dataType" : "binary",
        "data": "<base64_binary>"
    }
    

Resposta de mensagem em streaming

Quando uma mensagem pertence a um fluxo, a mensagem do grupo contém uma stream propriedade. O confiável sequenceId permanece com escopo de conexão e é diferente de stream.streamSequenceId.

{
    "sequenceId": 1,
    "type": "message",
    "from": "group",
    "group": "<group_name>",
    "dataType": "json|text|binary",
    "data": {},
    "fromUserId": "abc",
    "stream": {
        "streamId": "<stream_id>",
        "streamSequenceId": 1,
        "endOfStream": true,
        "error": {
            "name": "IdleTimeout|InternalServerError|Forbidden|Cancelled|UserError",
            "message": "<error_detail>",
            "userErrorCode": "<application_error_code>"
        }
    }
}
  • stream.streamId é o identificador lógico do fluxo.
  • stream.streamSequenceId é o número de sequência da mensagem no fluxo.
  • stream.endOfStream é opcional. Quando definido para true, a mensagem é a mensagem terminal do fluxo.
  • stream.error é opcional e está presente apenas quando o fluxo termina com um erro. userErrorCode está presente apenas para UserError.

Resposta de fluxo de água

O serviço envia uma streamAck resposta para confirmar dados de fluxo aceitos e para reportar o próximo ID de sequência de fluxo que espera.

Formato:

{
    "type": "streamAck",
    "streamId": "<stream_id>",
    "expectedSequenceId": 2
}

Resposta do nack do riacho

O serviço envia uma streamNack resposta para um erro de fluxo retentável.

Formato:

{
    "type": "streamNack",
    "streamId": "<stream_id>",
    "expectedSequenceId": 2,
    "name": "InvalidSequenceId|TransientError",
    "message": "<error_detail>"
}

Resposta fechada por fluxo

O serviço envia uma streamClosed resposta quando o fluxo do lado da editora é fechado.

Formato:

{
    "type": "streamClosed",
    "streamId": "<stream_id>",
    "error": {
        "name": "StreamNotFound|Forbidden|BadRequest|InternalServerError|IdleTimeout",
        "message": "<error_detail>"
    }
}

A error propriedade é omitida quando o fluxo de água está normalmente fechado.

Resposta do sistema

O serviço Web PubSub também pode retornar respostas relacionadas ao sistema para o cliente.

Resposta Pong

O serviço Web PubSub envia uma mensagem de pong para o cliente quando ele recebe uma mensagem de ping do cliente.

Formato:

{
    "type": "pong",
}

Conectado

A resposta à solicitação de conexão do cliente:

{
    "type": "system",
    "event": "connected",
    "userId": "user1",
    "connectionId": "abcdefghijklmnop",
    "reconnectionToken": "<token>"
}

connectionId e reconnectionToken são usados para reconexão. Faça a solicitação de conexão com URI para reconexão:

wss://<service-endpoint>/client/hubs/<hub>?awps_connection_id=<connectionId>&awps_reconnection_token=<reconnectionToken>

Encontre mais detalhes na Recuperação de Conexão

Desconectado

Quando o servidor fecha a conexão ou quando o serviço recusa a conexão do cliente:

{
    "type": "system",
    "event": "disconnected",
    "message": "reason"
}

Próximas etapas

Use estes recursos para começar a criar seu aplicativo: