하위 프로토콜을 사용하여 신뢰할 수 있는 Websocket 만들기

간헐적인 네트워크 문제로 인해 Websocket 클라이언트 연결이 끊어지면 메시지가 손실될 수 있습니다. 게시자/하위 시스템에서 게시자는 구독자와 분리되어 있으므로 게시자는 구독자의 연결이 끊어지거나 메시지가 손실되는 것을 감지하지 못할 수 있습니다. 클라이언트는 간헐적인 네트워크 문제를 극복하고 메시지 전달의 안정성을 유지하는 것이 중요합니다. 이를 위해 신뢰할 수 있는 Azure Web PubSub 하위 프로토콜을 사용하여 신뢰할 수 있는 Websocket 클라이언트를 만들 수 있습니다.

신뢰할 수 있는 프로토콜

Web PubSub 서비스는 두 개의 신뢰할 수 있는 하위 프로토콜 json.reliable.webpubsub.azure.v1protobuf.reliable.webpubsub.azure.v1를 지원합니다. 클라이언트는 안정성을 확보하기 위해 하위 프로토콜의 게시자, 구독자, 복구 부분을 따라야 합니다. 하위 프로토콜을 제대로 구현하지 못하면 메시지가 예상대로 전달되지 않거나 프로토콜 위반으로 인해 서비스가 클라이언트를 종료할 수 있습니다.

간편한 방법 - 클라이언트 SDK 사용

신뢰할 수 있는 클라이언트를 만드는 가장 간단한 방법은 클라이언트 SDK를 사용하는 것입니다. 클라이언트 SDK는 Web PubSub 클라이언트 사양을 구현하고 기본적으로 json.reliable.webpubsub.azure.v1를 사용합니다. 빠른 시작은 클라이언트 간 게시/구독을 참조하세요.

어려운 방법 - 직접 구현

다음 자습서에서는 Web PubSub 클라이언트 사양 구현의 중요한 부분을 안내합니다. 이 가이드는 빠른 시작을 찾는 사람이 아니라 안정성을 달성하는 원칙을 알고 싶어하는 사람을 위한 것입니다. 빠르게 시작하려면 클라이언트 SDK를 사용하세요.

초기화

신뢰할 수 있는 하위 프로토콜을 사용하려면 Websocket 연결을 구성할 때 하위 프로토콜을 설정해야 합니다. JavaScript에서 다음 코드를 사용할 수 있습니다.

  • Json 신뢰할 수 있는 하위 프로토콜 사용:

    var pubsub = new WebSocket(
      "wss://test.webpubsub.azure.com/client/hubs/hub1",
      "json.reliable.webpubsub.azure.v1"
    );
    
  • Protobuf 신뢰할 수 있는 하위 프로토콜 사용:

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

연결 복구

연결 복구는 안정성을 달성하는 기초이며 json.reliable.webpubsub.azure.v1protobuf.reliable.webpubsub.azure.v1 프로토콜을 사용할 때 구현되어야 합니다.

Websocket 연결은 TCP를 사용합니다. 연결이 끊어지지 않으면 메시지는 무손실이며 순서대로 전달됩니다. 연결 끊김으로 인한 메시지 손실을 방지하기 위해 Web PubSub 서비스는 그룹 및 메시지 정보를 포함하여 연결 상태 정보를 유지합니다. 이 정보는 연결 복구에서 클라이언트를 복원하는 데 사용됩니다.

클라이언트가 신뢰할 수 있는 하위 프로토콜을 사용하여 서비스에 다시 연결하면 클라이언트는 connectionIdreconnectionToken을 포함하는 Connected 메시지를 받게 됩니다. connectionId는 서비스에서 연결 세션을 식별합니다.

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

WebSocket 연결이 끊어지면 클라이언트는 동일한 connectionId로 연결을 다시 시도하여 동일한 세션을 복원해야 합니다. 클라이언트는 서버와 협상하고 access_token을 가져올 필요가 없습니다. 대신 연결을 복구하려면 클라이언트가 서비스 호스트 이름, connection_id, reconnection_token을 사용하여 서비스에 직접 WebSocket 연결 요청을 만들어야 합니다.

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

네트워크 문제가 아직 복구되지 않은 경우 연결 복구에 실패할 수 있습니다. 클라이언트는 다음까지 다시 연결하기 위해 계속 다시 시도해야 합니다.

  1. 상태 코드 1008로 Websocket 연결이 닫혔습니다. 상태 코드는 connectionId가 서비스에서 제거되었음을 의미합니다.
  2. 복구 실패가 1분 이상 계속 발생합니다.

게시자

이벤트 처리기에 이벤트를 보내거나 다른 클라이언트에 메시지를 게시하는 클라이언트를 게시자라고 합니다. 게시자는 메시지를 게시하는 데 성공했는지 여부에 대한 승인을 Web PubSub 서비스에서 받도록 메시지에서 ackId를 설정해야 합니다.

ackId는 메시지의 식별자이며, 각 새 메시지는 고유 ID를 사용해야 합니다. 메시지를 다시 전송할 때 원본 ackId를 사용해야 합니다.

샘플 그룹 보내기 메시지:

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

샘플 ack 응답:

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

Web PubSub 서비스가 success: true를 사용하여 ack 응답을 반환하는 경우 메시지는 서비스에서 처리되었으며 클라이언트는 메시지가 모든 구독자에게 전달될 것으로 예상할 수 있습니다.

서비스에서 일시적인 내부 오류가 발생하고 메시지를 구독자에게 보낼 수 없는 경우 게시자는 success: false가 포함된 ack를 받게 됩니다. 게시자는 오류를 읽어 메시지를 다시 전송할지 여부를 결정해야 합니다. 메시지가 다시 전송되면 동일한 ackId 메시지를 사용해야 합니다.

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

Message Failure

WebSocket 연결이 끊겨 서비스의 ack 응답이 손실된 경우 게시자는 복구 후 동일한 ackId로 메시지를 다시 보내야 합니다. 이전에 서비스에서 메시지를 처리한 경우 Duplicate 오류가 포함된 ack를 보냅니다. 게시자는 이 메시지를 다시 보내는 작업을 중지해야 합니다.

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

Message duplicated

구독자

이벤트 처리기 또는 게시자에서 보낸 메시지를 받는 클라이언트를 구독자라고 합니다. 네트워크 문제로 인해 연결이 끊어지면 Web PubSub 서비스는 구독자에게 전송된 메시지 수를 알 수 없습니다. 구독자가 마지막으로 받은 메시지를 확인하기 위해 서비스는 sequenceId가 포함된 데이터 메시지를 보냅니다. 구독자는 시퀀스 ack 메시지로 응답합니다.

샘플 시퀀스 ack:

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

sequenceId는 connection-id 세션에서 증가하는 uint64 번호입니다. 구독자는 받은 가장 큰 sequenceId을 기록하고, 더 큰 sequenceId가 있는 메시지만 수락하고, 더 작거나 같은 sequenceId가 있는 메시지를 삭제해야 합니다. 구독자는 서비스에서 구독자가 이미 받은 메시지 재전달을 건너뛸 수 있도록 기록된 가장 큰 sequenceId를 ack해야 합니다. 예를 들어 구독자가 sequenceId: 5를 사용하여 sequenceAck에 응답하는 경우 서비스는 sequenceId 5보다 큰 메시지만 다시 보냅니다.

WebSocket 연결이 끊어질 때까지 모든 메시지가 구독자에게 전달됩니다. sequenceId를 사용하면 서비스에서 구독자가 세션에서 WebSocket 연결 간의 받은 메시지 개수를 알 수 있습니다. WebSocket 연결이 끊어지면 서비스는 구독자가 승인하지 않은 메시지를 다시 전달합니다. 서비스는 제한된 수의 승인되지 않은 메시지를 저장합니다. 메시지 수가 제한을 초과하면 서비스에서 WebSocket 연결을 닫고 세션을 제거합니다. 따라서 구독자는 최대한 빨리 sequenceId를 ack해야 합니다.