Creare un websocket affidabile con sottoprotocol

Le connessioni client Websocket possono venire interrotte a causa di problemi di rete intermittenti e, quando le connessioni vengono interrotte, i messaggi andranno persi. In un sistema pubsub, gli editori sono separati dai sottoscrittori, quindi gli editori sono difficili da rilevare la perdita di messaggi o l'eliminazione dei sottoscrittori. È fondamentale che i client superino i problemi di rete intermittenti e mantengano l'affidabilità del recapito dei messaggi. A tale scopo, è possibile creare un client Websocket affidabile con l'aiuto di sottoprotocoli affidabili.

Nota

I protocolli reliable sono ancora in anteprima. Alcune modifiche sono previste in futuro.

Reliable Protocol

Il servizio supporta due sottoprotocoli json.reliable.webpubsub.azure.v1 affidabili e protobuf.reliable.webpubsub.azure.v1. I client devono seguire il protocollo, inclusa principalmente la parte della riconnessione, del server di pubblicazione e del sottoscrittore per ottenere l'affidabilità oppure il recapito dei messaggi potrebbe non funzionare come previsto o il servizio potrebbe terminare il client perché viola la specifica del protocollo.

Inizializzazione

Per usare sottoprotocol reliable, è necessario impostare subprotocol durante la creazione di connessioni Websocket. In JavaScript è possibile usare come segue:

  • Usare il sottoprotocolo Reliable Json

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

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

Riconnessione

Le connessioni Websocket inoltrino su TCP, quindi se la connessione non viene eseguita, tutti i messaggi devono essere senza perdita di dati e in ordine. Quando si verificano problemi di rete e connessioni si rilasciano, tutti gli stati, ad esempio le informazioni sul gruppo e sui messaggi, vengono mantenuti dal servizio e attendono il ripristino della riconnessione. Una connessione Websocket è proprietaria di una sessione nel servizio e l'identificatore è connectionId. La riconnessione è la base per ottenere l'affidabilità e deve essere implementata. Quando una nuova connessione si connette al servizio usando sottoprotocoli reliable, la connessione riceverà un Connected messaggio contenente connectionId e reconnectionToken.

{
    "type":"system",
    "event":"connected",
    "connectionId": "<connection_id>",
    "reconnectionToken": "<reconnection_token>"
}

Una volta interrotta la connessione WebSocket, il client deve prima provare a riconnettersi con lo stesso connectionId per mantenere la sessione. I client non devono negoziare con il server e ottenere .access_token Al contrario, la riconnessione deve effettuare una richiesta di connessione Websocket al servizio direttamente con connection_id e reconnection_token con l'URI seguente:

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

La riconnessione potrebbe non riuscire perché il problema di rete non è stato ancora recuperato. Il client deve continuare a riprovare a riconnettersi fino a quando

  1. Connessione Websocket chiusa con codice di stato 1008. Il codice di stato indica che connectionId è stato rimosso dal servizio.
  2. L'errore di riconnessione mantiene più di 1 minuto.

Publisher

I client che inviano eventi al gestore eventi o pubblicano messaggi ad altri client vengono chiamati autori nel documento. I server di pubblicazione devono impostare ackId sul messaggio per confermare dal servizio se il messaggio ha esito positivo o negativo. Il ackId nel messaggio è l'identificatore del messaggio, il che significa che i diversi messaggi devono usare s diversi ackId, mentre il messaggio di nuovo invio deve mantenere lo stesso ackId per il servizio da annullare.

Messaggio di invio di un gruppo di esempio:

{
    "type": "sendToGroup",
    "group": "group1",
    "dataType" : "text",
    "data": "text data",
    "ackId": 1
}

Risposta ack di esempio:

{
    "type": "ack",
    "ackId": 1,
    "success": true
}

Se il servizio restituisce ack con success: true, il messaggio è stato elaborato dal servizio e il client può aspettarsi che il messaggio venga recapitato a tutti i sottoscrittori.

In alcuni casi, tuttavia, Service soddisfa un errore interno temporaneo e il messaggio non può essere inviato al sottoscrittore. In questo caso, il server di pubblicazione riceverà un messaggio simile al seguente e dovrà inviare nuovamente un messaggio con lo stesso ackId valore se necessario in base alla logica di business.

{
    "type": "ack",
    "ackId": 1,
    "success": false,
    "error": {
        "name": "InternalServerError",
        "message": "Internal server error"
    }
}

Message Failure

L'ack del servizio può essere eliminato a causa dell'eliminazione della connessione WebSocket. Pertanto, gli editori devono ricevere una notifica quando la connessione Websocket viene eliminata e inviare nuovamente un messaggio con lo stesso ackId dopo la riconnessione. Se il messaggio è stato effettivamente elaborato dal servizio, risponderà ack con Duplicate e i server di pubblicazione smetteranno di inviare di nuovo il messaggio.

{
    "type": "ack",
    "ackId": 1,
    "success": false,
    "error": {
        "name": "Duplicate",
        "message": "Message with ack-id: 1 has been processed"
    }
}

Message duplicated

Sottoscrittore

I client che ricevono messaggi inviati da gestori eventi o server di pubblicazione vengono chiamati sottoscrittori nel documento. Quando le connessioni diminuiscono per problemi di rete, il servizio non sa quanti messaggi vengono effettivamente inviati ai sottoscrittori. Pertanto, i sottoscrittori devono indicare al servizio quale messaggio è stato ricevuto. I messaggi di dati contengono sequenceId e i sottoscrittori devono aggiungere l'ID sequenza con un messaggio sequence ack:

Ack sequenza di esempio:

{
    "type": "sequenceAck",
    "sequenceId": 1
}

Sequence-id è un numero incrementale uint64 con una sessione connection-id. I Sottoscrittori devono registrare l'ID sequenza più grande ricevuto e accettare tutti i messaggi con ID sequenza più grande e eliminare tutti i messaggi con ID sequenza più piccolo o uguale. L'ack sequenza supporta l'ack cumulativo, ovvero se si esegue l'ack sequence-id=5, il servizio tratterà tutti i messaggi con ID sequenza inferiore a 5 già ricevuti dai sottoscrittori. I sottoscrittori devono ottenere il valore di id sequenza più grande registrato, in modo che il servizio possa ignorare i messaggi di rollforward che i sottoscrittori hanno già ricevuto.

Tutti i messaggi vengono recapitati ai sottoscrittori in ordine fino a quando la connessione WebSocket non viene eliminata. Con sequence-id, il servizio può avere le informazioni sul numero di sottoscrittori di messaggi effettivamente ricevuti tra connessioni WebSocket con una sessione connection-id. Dopo l'eliminazione di una connessione WebSocket, il servizio ridistribuirà i messaggi che deve recapitare, ma non ack-ed dal sottoscrittore. Il servizio contiene messaggi che non sono ack-ed con limite, se i messaggi superano il limite, il servizio chiuderà la connessione WebSocket e rimuoverà la sessione connection-id. Di conseguenza, i sottoscrittori devono ottenere il valore sequence-id il prima possibile.