Sous-protocole WebSocket JSON fiable par Azure Web PubSub

Le sous-protocole JSON WebSocket, json.reliable.webpubsub.azure.v1, permet un échange de grande fiabilité de messages de publication ou d’abonnement directement entre les clients via le service sans faire d’aller-retour vers le serveur en amont.

Ce document décrit le sous-protocole json.reliable.webpubsub.azure.v1.

Lorsque les connexions clientes WebSocket sont supprimées à cause de problèmes de réseau occasionnels, les messages peuvent être perdus. Dans un système pub/sub, les publieurs sont découplés des abonnés et peuvent ne pas détecter les coupures de connexion ou les pertes de messages des abonnés.

Pour résoudre les problèmes de réseau occasionnels et garder la fiabilité de distribution de messages, vous pouvez utiliser le sous-protocole Azure WebPubSub json.reliable.webpubsub.azure.v1 pour créer un client PubSub WebSocket fiable.

Un client WebSocket PubSub fiable peut :

Par exemple, vous pouvez créer un client WebSocket PubSub fiable avec le code JavaScript suivant :

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

Consultez Comment créer des clients fiables pour implémenter la fiabilité des reconnexions et des messages pour les clients des éditeurs et des abonnés.

Lorsque le client utilise ce sous-protocole, les trames de données sortantes et entrantes contenir des charges utiles JSON.

autorisations

Un client PubSub WebSocket peut publier sur d’autres clients seulement s’il est autorisé. Les roles attribués au client déterminent les autorisations accordées au client :

Rôle Autorisation
Non spécifié(e) Le client peut envoyer des requêtes d’événements.
webpubsub.joinLeaveGroup Le client peut rejoindre/quitter n’importe quel groupe.
webpubsub.sendToGroup Le client peut publier des messages dans n’importe quel groupe.
webpubsub.joinLeaveGroup.<group> Le client peut rejoindre/quitter le groupe <group>.
webpubsub.sendToGroup.<group> Le client peut publier des messages sur le groupe <group>.
webpubsub.joinLeaveGroups.<pattern> Le client peut joindre/quitter n’importe quel groupe dont le nom correspond <pattern> (voir Modèles de rôle de groupe générique).
webpubsub.sendToGroups.<pattern> Le client peut publier des messages sur n’importe quel groupe dont le nom correspond <pattern> (voir Modèles de rôle de groupe générique).

Le serveur peut accorder ou révoquer les autorisations d’un client de manière dynamique en utilisant des API REST ou des SDK de serveur.

Note

Les rôles génériques (par exemple, webpubsub.sendToGroups.<pattern>) ne sont pas pris en charge dans les API REST ou les kits SDK serveur pendant l’exécution.

Demandes

Rejoindre des groupes

Format :

{
    "type": "joinGroup",
    "group": "<group_name>",
    "ackId" : 1
}
  • ackId est l’identité de chaque demande et doit être unique. Le service envoie un message de réponse Ack pour notifier le résultat du processus de la demande. Pour plus d’informations, consultez AckId et Ack Response

Quitter des groupes

Format :

{
    "type": "leaveGroup",
    "group": "<group_name>",
    "ackId" : 1
}
  • ackId est l’identité de chaque demande et doit être unique. Le service envoie un message de réponse Ack pour notifier le résultat du processus de la demande. Pour plus d’informations, consultez AckId et Ack Response

Publier des messages

Format :

{
    "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 est l’identité de chaque demande et doit être unique. Le service envoie un message de réponse Ack pour notifier le résultat du processus de la demande. Pour plus d’informations, consultez AckId et Ack Response
  • noEcho est facultatif. Si la valeur est true, ce message n’est pas renvoyé à la même connexion. Si elle n’est pas définie, la valeur par défaut est false.
  • dataType peut être défini sur json, text ou binary :
    • json : les data peuvent être de n’importe quel type pris en charge par JSON et seront publiées telles quelles. Si dataType n’est pas spécifié, ce sera json par défaut.
    • text : les data doivent être au format chaîne et les données de chaîne seront publiées ;
    • binary : les data doivent être au format base64, et les données binaires seront publiées ;

Cas 1 : publier des données texte :

{
    "type": "sendToGroup",
    "group": "<group_name>",
    "dataType" : "text",
    "data": "text data",
    "ackId": 1
}
  • Les clients du sous-protocole dans <group_name> reçoivent :
{
    "type": "message",
    "from": "group",
    "group": "<group_name>",
    "dataType" : "text",
    "data" : "text data"
}
  • Les clients WebSocket simples dans <group_name> reçoivent la chaîne text data.

Cas 2 : publier des données JSON :

{
    "type": "sendToGroup",
    "group": "<group_name>",
    "dataType" : "json",
    "data": {
        "hello": "world"
    }
}
  • Les clients du sous-protocole dans <group_name> reçoivent :
{
    "type": "message",
    "from": "group",
    "group": "<group_name>",
    "dataType" : "json",
    "data" : {
        "hello": "world"
    }
}
  • Les clients WebSocket simples dans <group_name> reçoivent la chaîne sérialisée {"hello": "world"}.

Cas 3 : publier des données binaires :

{
    "type": "sendToGroup",
    "group": "<group_name>",
    "dataType" : "binary",
    "data": "<base64_binary>",
    "ackId": 1
}
  • Les clients du sous-protocole dans <group_name> reçoivent :
{
    "type": "message",
    "from": "group",
    "group": "<group_name>",
    "dataType" : "binary",
    "data" : "<base64_binary>", 
}
  • Les clients WebSocket simples dans <group_name> reçoivent les données binaires dans la trame binaire.

Commencez à diffuser les messages

Pour lancer un stream de groupe, envoyez une sendToGroup requête avec la stream propriété. Une requête de démarrage de flux ne contient datapas , dataType, ni ackId.

Format :

{
    "type": "sendToGroup",
    "group": "<group_name>",
    "noEcho": true|false,
    "stream": {
        "streamId": "<stream_id>",
        "idleTimeoutMs": 300000
    }
}
  • stream.streamId est l’identifiant du flux logique. Il doit s’agir d’une chaîne non vide et doit être unique parmi les flux actifs sur la même connexion client. Il est recommandé aux bibliothèques clients de générer une valeur globalement unique, comme un GUID ou un UUID.
  • stream.idleTimeoutMs est facultatif. Si spécifié, il doit être supérieur à 0. Si elle est omise, le service par défaut est 300000 des millisecondes. La valeur correspond à un délai d’attente inactif, pas à une durée totale de vie du flux. Envoyez les données du flux, envoyez un flux en mode « keep epalive », ou terminez le flux avant que ce délai d’expiration ne s’écoule, lorsque l’application doit garder le flux ouvert.
  • noEcho est facultatif. Si elle est réglée sur true, les messages du flux ne sont pas renvoyés vers la même connexion. Si elle n’est pas définie, la valeur par défaut est false.

Lorsque le flux est accepté, le client reçoit une réponse d’ack du flux avec expectedSequenceId un objectif fixé à 1.

Envoyer des données en streaming

Pour envoyer des données de flux, envoyez une streamData requête avec streamId, streamSequenceId, dataType, et data.

Format :

{
    "type": "streamData",
    "streamId": "<stream_id>",
    "streamSequenceId": 1,
    "dataType" : "json|text|binary",
    "data": {}
}
  • streamId identifie un flux actif sur la même connexion client.
  • streamSequenceId est un nombre uint64 positif. Le premier fragment de données d’un flux utilise 1, et chaque fragment de données suivant pour le même streamId s’augmente exactement 1de .
  • dataType peut être défini à json, text, ou binary, avec les mêmes règles d’encodage des données que les messages publiés.

Pour maintenir un flux actif sans fournir de données aux abonnés, envoyez une streamData requête avec seulement type et streamId.

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

Fin des messages en streaming

Pour terminer un stream, envoyez une streamEnd demande.

Format :

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

Pour terminer un flux avec une erreur définie par l’application, incluez la propriété optionnelle error .

{
    "type": "streamEnd",
    "streamId": "<stream_id>",
    "error": {
        "message": "<error_detail>",
        "userErrorCode": "<application_error_code>"
    }
}
  • error.message est un message d’erreur lisible par l’humain optionnel.
  • error.userErrorCode est un code d’erreur optionnel défini par l’application.

Lorsque le flux est fermé, l’éditeur reçoit une réponse de fermeture du flux.

Envoyer des événements personnalisés

Format :

{
    "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 est l’identité de chaque demande et doit être unique. Le service envoie un message de réponse Ack pour notifier le résultat du processus de la demande. Pour plus d’informations, consultez AckId et Ack Response

dataType peut être text, binary ou json :

  • json : les données peuvent être de n’importe quel type pris en charge par JSON et seront publiées telles quelles. Le type par défaut est json.
  • text : les données sont au format chaîne et les données de chaîne seront publiées ;
  • binary : les données sont au format base64, et les données binaires seront publiées ;

Cas 1 : envoyer un événement avec des données texte :

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

Le gestionnaire d’événements en amont reçoit des données semblables à :

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

Le Content-Type de la requête HTTP CloudEvents est text/plain lorsque dataType est text.

Cas 2 : envoyer un événement avec des données JSON :

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

Le gestionnaire d’événements en amont reçoit des données semblables à :

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"
}

Le Content-Type de la requête HTTP CloudEvents est application/json lorsque dataType est json

Cas 3 : envoyer un événement avec des données binaires :

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

Le gestionnaire d’événements en amont reçoit des données semblables à :

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

Le Content-Type de la requête HTTP CloudEvents est application/octet-stream lorsque dataType est binary. La trame WebSocket peut être au format text pour les trames de message texte ou de binaires codés en UTF8 pour les trames de message binary.

Le service Web PubSub refuse le client si le message ne correspond pas au format décrit.

Ping

Format :

{
    "type": "ping",
}

Le client peut envoyer un message ping au service pour permettre au service Web PubSub de détecter si le client est actif.

Accusé de réception de la séquence

Format :

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

Le client PubSub WebSocket fiable doit envoyer un message d’accusé de réception de la séquence une fois qu’il a reçu un message du service. Pour plus d’informations, consultez Guide pratique pour créer des clients fiables

  • sequenceId est un nombre uint64 incrémentiel issu du message reçu.

Réponses

Les messages reçus par le client peuvent être de plusieurs types : ack, message, system, pong, streamAck, streamNack, et streamClosed. Les messages avec le type message ont la propriété sequenceId. Le client doit envoyer un accusé de réception de la séquence au service une fois qu’il reçoit un message.

Réponse ACK

Quand la requête contient ackId, le service renvoie une réponse ACK pour cette requête. L’implémentation du client doit gérer ce mécanisme ACK, y compris l’attente de la réponse ACK utilisant une opération asyncawait, et possède un gestionnaire du délai d’attente lorsque la réponse ACK n’est pas reçue pendant une certaine période.

Format :

{
    "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>"
    }
}

L’implémentation du client DOIT toujours vérifier en premier si la success est true ou false. Ce n’est que lorsque success est false que le client lit error.

Réponse de message

Les clients peuvent recevoir des messages publiés par un groupe que le client a rejoint, ou par le serveur, qui opère en tant que rôle de gestionnaire du serveur, envoie des messages au client ou à l’utilisateur spécifique.

  1. Le message de réponse d’un groupe :

    {
        "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. Le message de réponse du serveur :

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

Cas 1 : envoi de données Hello World à la connexion via l’API REST avec Content-Type=text/plain

  • Un client WebSocket simple reçoit une trame WebSocket de texte avec des données : Hello World ;

  • Un client WebSocket PubSub reçoit le message en JSON :

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

Cas 2 : envoi de données { "Hello" : "World"} à la connexion via l’API REST avec Content-Type=application/json

  • Un client WebSocket simple reçoit une trame WebSocket de texte avec des données de chaîne : { "Hello" : "World"} ;

  • Un client WebSocket PubSub reçoit le message en JSON :

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

Si l’API REST envoie une chaîne Hello World avec le type de contenu application/json, le client WebSocket simple reçoit une chaîne JSON "Hello World" entouré par ".

Cas 3 : envoi de données binaires à la connexion via l’API REST avec Content-Type=application/octet-stream

  • Un client WebSocket simple reçoit une trame WebSocket binaire avec les données binaires.

  • Un client WebSocket PubSub reçoit le message en JSON :

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

Réponse aux messages en streaming

Lorsqu’un message appartient à un flux, le message de groupe contient une stream propriété. Le fiable sequenceId reste à portée de connexion et est différent 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 est l’identifiant logique du flux.
  • stream.streamSequenceId est le numéro de séquence du message dans le flux.
  • stream.endOfStream est facultatif. Lorsqu’il est réglé à true, le message est le message terminal du flux.
  • stream.error est optionnel et n’est présent que lorsque le flux se termine avec une erreur. userErrorCode est présent uniquement pour UserError.

Réponse des flux d’eau

Le service envoie une streamAck réponse pour accuser réceptionné des données de flux acceptées et pour signaler l’ID de séquence de flux suivant qu’il attend.

Format :

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

Réponse du ruisseau

Le service envoie une streamNack réponse pour une erreur de flux réessayable.

Format :

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

Réponse fermée par flux

Le service envoie une streamClosed réponse lorsque le flux côté éditeur est fermé.

Format :

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

Cette error propriété est omise lorsque le flux est normalement fermé.

Réponse du système

Le service Web PubSub peut renvoyer au client des réponses liées au système.

Réponse Pong

Le service Web PubSub envoie un message pong au client quand il reçoit un message ping de sa part.

Format :

{
    "type": "pong",
}

Connecté

La réponse à la requête de connexion du client :

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

connectionId et reconnectionToken sont utilisés pour la reconnexion. Faites une demande de connexion avec l’URI pour la reconnexion :

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

Pour plus d’informations, consultez La récupération de connexion

Déconnecté

La réponse lorsque le serveur ferme la connexion ou lorsque le service refuse la connexion client :

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

Étapes suivantes

Utilisez ces ressources pour commencer à créer votre propre application :