개발 네트워크 WebSocket 챕터 #2: 웹소켓 프로토콜
포스트
취소

WebSocket 챕터 #2: 웹소켓 프로토콜

웹소켓 프로토콜 (WebSocket Protocol)

WebSocket wire protocol (RFC 6455)

  • HyBi 워킹 그룹에서 개발
  • 상위 구성 요소 (2가지)
    • 연결 매개변수를 협상하는 데 사용되는 오프닝 HTTP 핸드셰이크 메커니즘
    • 텍스트 및 바이너리 데이터의 낮은 오버헤드, 메시지 기반 전송을 가능하게 하는 바이너리 메시지 프레임 메커니즘

웹소켓 프로토콜은 기존 양방향 HTTP 기술의 목표를 해결하려고 시도하며, 따라서 HTTP 포트 80 및 443에서 작동하도록 설계되었습니다. 하지만 웹소켓은 HTTP로 제한하지 않습니다. 때문에 전용 포트 사용하기도 가능하고 더 간단한 핸드셰이크를 사용할 수도 있습니다.

WebSocket Protocol, RFC 6455

웹소켓 프로토콜은 브라우저 외부에서 사용할 수 있는 독립형 프로토콜입니다.

이진 프레이밍 레이어 (Binary Framing Layer)

  • 발신자
    • 임의의 UTF-8 또는 바이너리 페이로드를 제공합니다.
  • 수신자
    • 전체 메시지를 사용할 수 있을 때 알림 받습니다.
  • 이를 위해 WebSocket은 사용자 지정 바이너리 프레임 형식(참조 1)을 사용
    애플리케이션 메시지를 하나 이상의 프레임으로 분할하고, 이를 대상으로 전송하고, 재조립한 후 전체 메시지가 수신되면 수신자에게 최종적으로 알립니다.
    웹소켓 통신은 이와 같은 프레임들을 교환하며 이루어집니다.

참조1 참조 1. WebSocket의 어플리케이션 메시지 프레임 구조

Frame (프레임)

가변 길이의 프레임 헤더와 메시지를 일부나 전부 전달할 수 있는 페이로드를 포함하는 가장 작은 통신 단위.

Message (메시지)

논리적 애플리케이션 메시지에 매핑되는 프레임들을 합친 것

어플리케이션 메시지 프레이밍

  • 메시지가 쪼개져서 프레임 단위가 되는 것을 의미함
  • 애플리케이션 메시지를 여러 프레임으로 분할할지 여부
  • 애플리케이션은 개별 웹소켓 프레임이나 프레이밍이 수행되는 방식을 알지 못함
    • 하지만 각 웹소켓 프레임이 와이어에서 어떻게 표현되는지 주요 내용을 이해하는 것은 여전히 유용
  • 각 프레임의 첫번째 비트(FIN) = 메시지 최종조각 여부
    • 메시지는 단 하나의 프레임으로 구성될 수 있음
  • Opcode(4비트) = 전송된 프레임의 유형, 제어프레임
    • 애플리케이션 데이터 전송을 위한 텍스트(1)
    • 바이너리(2)
    • 연결 상태 확인을 위한 연결 닫기(8)
    • 핑(9)
    • 퐁(10)
  • 마스크 비트
    • 페이로드의 마스크 여부를 나타냄
    • 마스크란 보안 기술로 개인을 식별할 수 있는 데이터를 보이지 않게 처리하는 것
    • 페이로드 마스킹의 목적
      • 데이터 패킷의 패턴을 숨기고 위해 있음
      • 프록시 서버와 같은 중간자가 트래픽을 분석하고 패턴을 찾는 것을 막기 위함임. (연산비용이 높음)
      • 기본적인 방어책임, 데이터 보안 수준을 높이기 위한 완벽한 해결책 x
    • 클라이언트에서 서버로 전송되는 메시지에만 해당
  • 페이로드 길이 (마스크 비트를 빼고 7비트로 표시, 또한 가변 길이 필드로 표시)
    • 0-125면 페이로드 길이를 뜻함
    • 126이면 다음 2바이트가 프레임 길이를 나타내는 16비트 부호 없는 정수
    • 127이면 다음 8바이트가 프레임 길이를 나타내는 64비트 부호 없는 정수
    • 즉 7bit의 페이로드 길이 값에 따라 그림 1의 Extended length를 얼마나 쓸지를 정함
  • 마스킹 키
    • 페이로드를 마스킹하는 데 사용되는 32비트 값이 포함

      모든 클라이언트에서 페이로드가 마스킹 되는 것은 캐시 포이즈닝 공격(cache poisoning attack)을 방지할 수 있음. 공격에 대한 자세한 내용은 W2SP 2011에서 발표된 “Talking to Yourself for Fun and Profit”를 참조하세요. 해당 내용이 난해하다면 이 블로그의 게시글도 확인해보세요.

  • 페이로드
    • 연결이 설정될 때 클라이언트와 서버가 확장자를 협상한 경우 애플리케이션 데이터와 사용자 지정 확장자 데이터가 포함

결과적으로 각 웹소켓 프레임에는 2~10바이트의 프레임 오버헤드가 발생.

클라이언트는 웬만하면 항상 마스킹 키를 전송해야 하고 헤더에 4바이트가 추가되어 6~14바이트의 오버헤드가 발생. 이 오버헤드들은 모두 트래픽과 대역폭 제한에 영향을 줌

  • WebSocket Multiplexing과 Head-of-Line Blocking 메시지는 하나 이상의 프레임으로 분할될 수 있기 때문에 웹소켓은 head-of-line blocking에 취약. HTTP/2 프레이밍 메커니즘에 “stream ID”에 해당하는 것이 없기 때문에 서로 다른 메시지의 프레임은 인터리빙(끼워넣기) 안됨 (Streams, Messages, and Frames 참조) 대용량 메시지는 여러 개의 웹소켓 프레임으로 분할되어 있더라도 다른 메시지와 관련된 프레임의 전송을 차단합니다. -> 애플리케이션이 지연 시간에 민감한 데이터를 전송하는 경우 주의 필요. 메시지 분할 등을 활용 HyBi 워킹 그룹에서 개발한 새로운 “웹소켓용 멀티플렉싱 확장”

    이 확장 기능을 사용하면 하나의 TCP 연결이 채널 ID로 태그된 프레임을 캡슐화하여 여러 개의 가상 웹소켓 연결을 제공할 수 있습니다… 멀티플렉싱 확장 기능은 별도의 논리 채널을 유지하며, 각 채널은 별도의 핸드셰이크 헤더를 포함하여 독립적인 웹소켓 연결과 논리적으로 완전히 동등한 기능을 제공합니다. WebSocket Multiplexing (Draft 10) 이 확장 기능(HTTP/1 용)을 사용하면 동일한 TCP 연결을 통해 여러 웹소켓 연결(채널)을 멀티플렉싱할 수 있음. HTTP/2는 스트림 멀티플렉싱이 내장되어 있고 HTTP/2 프레이밍 메커니즘 내에서 웹소켓 프레임을 캡슐화하여 단일 세션 내에서 여러 개의 웹소켓 연결을 전송할 수 있습니다. HTTP/2에서의 WebSocket은 RFC 8441에 정의되어 있습니다. 결론: HTTP/2가 웹소켓 사양에서 고려할 것이 적다.

프로토콜 확장 (Protocol Extension)

  • WebSocket은 프로토콜 확장을 허용함
  • 연결 포맷과 의미는 새로운 연산 코드와 데이터 필드로 확장 가능.
  • 클라이언트와 서버가 애플리케이션 코드 없이 기본 웹소켓 프레이밍 계층 위에 추가 기능 구현 가능
  • 클라이언트는 초기 업그레이드 핸드셰이크에서 확장 활성 여부를 알리고 서버는 협상된 연결의 수명 동안 사용할 확장을 선택하고 승인해야 함

대신 웹소켓에서 헤더 필드페이로드에 대한 기타 정보와 같은 다른 메타데이터는 사용할 수 없습니다.

WebSocket 프로코콜 확장의 예(By HyBi Working Group)

  • A Multiplexing Extension for WebSockets
    • 별도의 논리적 웹소켓 연결이 기본 전송 연결을 공유할 수 있는 방법 제공.

각 웹소켓 연결에는 전용 TCP 연결이 필요하므로 비효율적이기에 이 확장은 각 웹소켓 프레임에 “channel ID”를 추가하여 여러 가상 웹소켓 채널이 단일 TCP 연결을 공유해 해결합니다.

  • Compression Extensions for WebSocket
    • 웹소켓 프로토콜에 압축 기능을 추가하는 웹소켓 확장을 만들기 위한 프레임워크

과거에 기본 웹소켓 사양은 전송된 데이터의 압축을 위한 메커니즘이나 조항을 제공하지 않았을 때 제공되었던 확장입니다. 각 프레임은 애플리케이션에서 제공하는 페이로드 데이터를 전달합니다. 바이트 전송에서 압축 기술이 적용되지 않아 전송 오버헤드가 발생했고 압축 확장은 HTTP에서 제공하는 전송-인코딩 협상과 비슷합니다.

  • 웹소켓 멀티플렉싱과 RFC 7692 HTTP/2 가 도입되면서 대부분의 브라우저가 웹소켓 멀티플렉싱을 지원합니다. 이 글의 원본이 적혔던 2013년에서 2년이 지난 2015년 12월 RFC 7692가 제정되어 WebSocket 프로토콜의 압축 통신에 대한 사양이 기술되었습니다. 프레임별 압축은 페이로드 콘텐츠를 프레임 단위로 압축하므로 여러 프리엠으로 분할될 수 있는 대용량 메시지에는 적합하지 않습니다. 따라서 RFC 7692에선 메시지별 압축을 적용하는 방식에 대해 기술하고 있으며 Deflate 알고리즘을 이용한 압축을 설명하고 있습니다. 현재 다양한 websocket 라이브러리들이 이를 지원하고 있고 이를 활성화 하면 웹소켓으로 발생하는 트래픽의 80% 가량을 절감할 수 있다고 한 라이브러리에서 설명하고 있습니다.

HTTP 업그레이드 협상(HTTP Upgrade Negotiation)

WebSocket protocol

  • 메시지 지향 통신
  • 자체 바이너리 프레이밍 계층
  • 하위 프로토콜 협상
  • 선택적 프로토콜 확장

웹소켓 프로토콜은 위와 같은 강력한 기능을 제공합니다.

하지만 메시지를 교환하기 전에 클라이언트와 서버는 연결을 설정하기 위해 적절한 매개변수로 협상해야 합니다.

HTTP를 활용하여 Handshake(핸드셰이크, 협상 과정)를 수행하면 아래와 같은 이점이 있습니다.

(처음에 얘기했듯 웹소켓은 HTTP 프로토콜에 한정되지 않습니다)

  1. 기존 HTTP 인프라 호환

    웹소켓 서버는 클라이언트(브라우저)에서 유일하게 열려 있는 포트인 80번과 443번 포트에서 실행할 수 있습니다.

  2. 협상을 수행하기 위해 커스텀 웹소켓 헤더를 사용하여 HTTP Upgrade 플로우를 재사용하고 확장할 수 있습니다.

아래는 협상을 위해 HTTP Upgrade 플로우에서 사용하는 매개변수들입니다.

Sec-WebSocket-Version

클라이언트가 사용하려는 웹소켓 프로토콜의 버전(RFC6455의 경우 “13”)을 나타냄. 서버가 이 버전을 지원하지 않으면 지원되는 버전 목록과 함께 응답.

Sec-WebSocket-Key

클라이언트가 전송하는 자동 생성 키. 서버가 요청된 버전의 프로토콜을 지원한다는 것을 증명하기 위해 서버에 호환성 검증 요청.

Sec-WebSocket-Accept

Sec-WebSocket-Key의 서명된 값을 포함함.

요청된 프로토콜 버전을 이해하고 있음을 증명하는 서버 응답임.

Sec-WebSocket-Protocol

애플리케이션 하위 프로토콜을 협상하는 데 사용.

클라이언트는 지원되는 프로토콜 목록을 알리고 서버는 단일 프로토콜 이름으로 응답함.

Sec-WebSocket-Extensions

웹소켓 확장을 사용할지 여부를 협상할 때 사용 클라이언트는 지원되는 확장을 알리고 서버는 동일한 헤더를 반환하여 사용할 리스트를 확인함.

HTTP을 웹소켓으로 업그레이드 하는 클라이언트 요청

1
2
3
4
5
6
7
8
9
GET /socket HTTP/1.1
Host: server.example.com
Connection: Upgrade                                                     #1
Upgrade: websocket                                                      #2
Sec-WebSocket-Key: eGsdHGKsxBFSzDBe23djsS==                             #3
Sec-WebSocket-Protocol: chat, multichat                                 #4
Sec-WebSocket-Version: 13                                               #5
Sec-WebSocket-Extensions: x-custom-extension1, x-custom-extension1      #6
Origin: http://example.com
  1. 서버에 다른 프로토콜로 변경 요청
  2. 웹소켓 프로토콜로 업그레이드 요청
  3. 서버 프로토콜 지원 확인을 위한 자동 생성 키
  4. 애플리케이션에서 지정한 하위 프로토콜의 선택적 목록
  5. 클라이언트에서 사용하는 웹소켓 프로토콜 버전
  6. 클라이언트가 지원하는 프로토콜 확장 목록(선택 사항)

(위는 순서대로 적용되는 것이 아닙니다)

웹소켓 요청에는 same-origin 정책이 적용됩니다.

브라우저는 upgrade 핸드셰이크에 origin 헤더를 추가하고 원격 서버는 CORS를 사용해 요청을 수락하거나 거부할 수 있습니다(Cross-Origin Resource Sharing (CORS) 참조).

서버저가 클라이언트에 웹소켓으로 전환 됐음을 알리는 응답

1
2
3
4
5
6
7
HTTP/1.1 101 Switching Protocols                    #1
Connection: Upgrade                                 #2
Upgrade: websocket                                  #3
Access-Control-Allow-Origin: http://example.com     #4
Sec-WebSocket-Accept: eFdPsjqiIjqh73FJAlzEsd=AKao=  #5
Sec-WebSocket-Protocol: multichat                   #6
Sec-WebSocket-Extensions: x-custom-extension1       #7
  1. 웹소켓 업그레이드를 확인하는 101 응답 코드
  2. 클라이언트에 연결이 다른 프로토콜로 변경됨을 알림
  3. 웹소켓 프로토콜로 업그레이드 요청
  4. 교차 출처 연결에 대한 옵트인(opt-in)을 나타내는 CORS 헤더
  5. 서명된 키 값 증명 프로토콜 지원
  6. 서버가 선택한 애플리케이션 하위 프로토콜
  7. 서버가 선택한 웹소켓 확장 목록(없을 수 있음)

모든 RFC6455 기반 웹소켓 서버는 동일한 방식으로 클라이언트의 호환성 검증 요청에 응답을 생성합니다. 서버는 Sec-WebSocket-Key 값을 표준에서 명시된 고유 GUID 문자열과 결합하여 SHA1 해시를 계산하고, 그 결과를 Base-64로 인코딩하여 클라이언트에 반환합니다.

웹소켓 핸드셰이크를 성공적으로 수행하려면, 최소한 프로토콜 버전과 클라이언트가 전송한 자동 생성된 호환성 검증 값이 포함되어야 합니다.

또한, 선택한 프로토콜 버전을 확인하기 위해, 서버는 해시된 챌린지-응답이 포함된 101 HTTP 응답 코드를 전송해야 합니다.

핸드셰이크가 정상적으로 완료되면, 연결이 WebSocket 메시지 전송을 위한 양방향 통신 채널로 사용될 수 있습니다. 이 시점부터는 HTTP 프로토콜 대신 WebSocket 프로토콜이 사용됩니다.

요청 및 응답 스트리밍

웹소켓은 동일한 TCP 연결을 통해 양방향 통신을 허용하는 사양에 정의된 유일한 프로토콜입니다.

참조2는 여러 통신 전송 방식이 어떤 구조와 방향으로 통신하는지에 대한 시각화입니다.

참조2 참조 2. XHR과 SSE, WebSocket의 연결 플로우

  • XHR은 요청-응답(request-response) 통신의 ‘트랜잭션’에 최적화되어 있습니다. 클라이언트는 서버에 완전한 HTTP 요청을 전달하고, 서버는 완전한 형식의 응답으로 응답합니다. 요청 스트리밍은 지원되지 않으며, 스트림 API가 도입되기 전에는 신뢰할 수 있는 브라우저 간 응답 스트리밍 API도 존재하지 않았습니다. XMLHttpRequest, fetch API, Axios와 같은 방식들이 REST API 요청에 사용되며, 이들은 모두 polling 방식의 요청 구조를 구현할 수 있습니다.
  • SSE를 사용하면 텍스트 기반 데이터의 효율적이고 지연 시간이 짧은 서버 간 스트리밍이 가능합니다. 클라이언트가 SSE 연결을 시작하고 서버는 이벤트 소스 프로토콜을 사용하여 업데이트를 클라이언트로 스트리밍합니다. 클라이언트는 초기 핸드셰이크 이후에는 서버에 어떠한 데이터도 전송할 수 없습니다.
  • 웹소켓은 클라이언트와 서버 간의 양방향 통신을 가능하게 하는 프로토콜입니다. 앞서 말했듯 텍스트와 바이너리 데이터를 실시간으로 송수신할 수 있습니다. 웹소켓은 초기 핸드셰이크를 통해 연결이 수립되며, 이후에는 지속적인 연결을 통해 데이터를 주고받습니다. 그 결과, 웹소켓은 빠른 데이터 교환과 낮은 지연 시간을 제공하며, 서버와 클라이언트 간의 상호 작용이 필요한 실시간 애플리케이션에 적합합니다.
  • 하지만 위 모두 전파 속도와 서버의 큐 대기시간에 종속적인 부분이 있습니다.

메시지 오버헤드

웹소켓으로 통신하는 메시지에는 오버헤드가 발생합니다.

애플리케이션 메시지

  • 하나 이상의 프레임으로 분할.
  • 각 프레임은 2바이트에서 14바이트의 오버헤드를 추가.
  • 프레임은 사용자 정의 바이너리 형식을 통해 이루어지기 때문에 UTF-8 및 바이너리 애플리케이션 데이터 모두 동일한 메커니즘을 통해 효율적으로 인코딩 됨.

WebScoket과 XHR 및 SSE와 비교

구분웹소켓 (WebSocket)XHR (XMLHttpRequest)SSE (Server-Sent Events)
통신 방식양방향단방향 (요청-응답)단방향 (서버 → 클라이언트)
메시지 형식텍스트, 바이너리텍스트, 바이너리텍스트
프레임 구조간결한 바이너리 프레임풀 HTTP 헤더풀 HTTP 헤더
지연 시간낮음중간중간
연결 상태지속적각 요청마다 연결지속적
전송 방식스트리밍요청-응답 기반스트리밍
오버헤드최소 2바이트 ~ 최대 14바이트헤더 크기 (일반적으로 500바이트 ~ 2KB),
요청별 연결
헤더 크기 (일반적으로 500바이트 ~ 2KB),
텍스트 전송만 가능
상세 정보헤더 크기 최소화,
지속적 연결
헤더 크기에 따른 높은 오버헤드,
요청별 연결
헤더 크기에 따른 중간 오버헤드,
이벤트 기반

이 오버헤드에는 애플리케이션 프로토콜에 관계없이 메시지당 60~100바이트의 오버헤드를 추가하는 IP, TCP, TLS 프레이밍의 오버헤드가 포함되어 있지 않습니다. (Optimize TLS Record Size 참조)

웹소켓, XHR, SSE의 데이터 효율성 및 압축 비교

웹소켓 (WebSocket)

웹소켓은 클라이언트와 서버 간의 양방향 통신을 효율적으로 처리하기 위해 설계되었습니다. 웹소켓의 데이터 효율성은 다음과 같은 방식으로 달성됩니다.

  1. 간결한 프레이밍: 웹소켓은 간결한 바이너리 프레이밍을 사용하여 메시지를 전송합니다. 이를 통해 웹소켓은 적은 양의 오버헤드로 메시지를 전송할 수 있습니다.
  2. 지속적인 연결: 웹소켓은 클라이언트와 서버 간에 지속적인 연결을 유지하므로, 새로운 요청 및 응답을 위한 연결 생성 및 해제 비용이 절약됩니다.

웹소켓은 ‘permessage-deflate’라는 확장을 사용하여 데이터 압축을 지원합니다. 이 확장을 사용하면 클라이언트와 서버는 압축된 데이터를 서로 주고받을 수 있습니다. 이를 통해 전송되는 데이터의 크기를 줄이고 대역폭을 절약할 수 있습니다.

XHR (XMLHttpRequest)

XHR은 요청-응답 통신 방식으로, 헤더를 전송하고 연결을 생성 및 해제하는 비용이 발생합니다. 이러한 특성 때문에 웹소켓에 비해 데이터 효율성이 떨어질 수 있습니다.

그러나 압축 측면에서는 XHR이 웹소켓과 유사한 성능을 제공합니다. XHR에서 전송되는 데이터는 HTTP 프로토콜에 의해 압축될 수 있습니다. 대표적인 HTTP 압축 방식으로는 gzip, deflate, brotli 등이 있습니다.

SSE (Server-Sent Events)

SSE는 서버에서 클라이언트로의 단방향 통신을 지원하며, 헤더 크기에 따른 오버헤드가 있습니다. SSE는 텍스트 기반 데이터 전송에 최적화되어 있으며, 연속적인 데이터 스트림에 적합합니다.

SSE에서 전송되는 데이터도 HTTP 프로토콜에 의해 압축될 수 있습니다. 따라서 XHR과 마찬가지로 SSE에서도 압축을 통해 데이터 크기를 줄이고 대역폭을 절약할 수 있습니다. SSE에서도 gzip, deflate, brotli 등의 HTTP 압축 방식을 사용할 수 있습니다.

결론

각 통신 방식의 데이터 효율성 및 압축 성능은 사용 사례에 따라 선택할 수 있으며, 각 방식의 장점을 최대한 활용해야 합니다. 웹소켓은 지속적인 양방향 통신이 필요한 경우, XHR은 트랜잭션 요청-응답 통신이 필요한 경우, SSE는 서버에서 클라이언트로의 지속적인 데이터 전송이 필요한 경우에 적합합니다.

커스텀 애플리케이션 프로토콜(Custom Application Protocol)

WebSocket은 기존의 HTTP 프로토콜과는 다른 양식을 가지며, 클라이언트와 서버 간에 사용자 지정된 프로토콜을 사용하여 데이터를 전송합니다. 이러한 이유로 WebSocket은 일종의 맞춤형 프로토콜로 간주되기도 합니다.

일반적으로, 스트리밍 서비스에서 WebSocket과 같은 프로토콜을 사용하면, 클라이언트와 서버 간에 실시간 양방향 통신을 제공할 수 있습니다. 이를 통해 더 높은 대역폭 사용률과 더 낮은 지연 시간을 제공할 수 있습니다. 또한, 맞춤형 프로토콜을 사용하면 데이터를 압축하거나, 메시지 전달을 최적화하거나, 세션 상태를 관리하는 등의 추가 기능을 구현할 수 있습니다.

그러나 WebRTC와 RTMP와 같은 다른 프로토콜도 사용될 수 있습니다. WebRTC는 실시간 비디오 및 오디오 통신을 위해 설계되었으며, P2P 통신 기술을 사용합니다. RTMP는 Adobe Flash 플레이어에서 사용되는 프로토콜로, 동영상 스트리밍에 사용됩니다.

브라우저는 HTTP 프로토콜에 최적화되어 있어서, XHR 요청과 같은 HTTP 기반의 요청에 대해서는 다양한 서비스를 제공합니다. 이러한 서비스에는 인증, 캐싱, 압축 등이 포함됩니다.

그러나 맞춤형 프로토콜을 사용하면 HTTP 프로토콜에서 제공되는 일부 서비스를 사용할 수 없다는 단점이 있습니다. 예를 들어, HTTP 캐시, 인증, 쿠키와 같은 서비스는 맞춤형 프로토콜에서 직접 구현해야 합니다.

웹소켓에서는 쿠키를 이용해서 초기의 핸드셰이크 연결 구성에서 사용자 인증을 진행할 수 있습니다.

WebSocket과 캐시 활용법

웹소켓은 CDN 등에서 캐시를 하지 못합니다. 하지만 일반 HTTP 통신은 캐싱이 됩니다.

효율적인 캐싱 전략을 위해 웹소켓은 제어 명령을 통신하고 이 제어 명령으로 캐시된 데이터를 불러오게 요청하는 방법을 사용할 수 있습니다.

웹소켓 인프라 배포 및 최적화 구성전략

웹소켓은 실시간 양방향 통신을 가능하게 하는 효과적인 기술입니다. 웹소켓 인프라 배포를 성공적으로 수행하려면 몇 가지 주요 사항을 고려해야 합니다. 이 글에서는 웹소켓 인프라 배포를 최적화하고 구성하는 방법에 대해 설명합니다.

  1. 내부 네트워크 조정 내부 네트워크의 라우터, 로드 밸런서, 프록시를 적절하게 구성하고 조정해야 웹소켓 세션의 수명이 길어질 수 있습니다. 이를 통해 오래 지속되는 연결을 지원하고 성능을 개선할 수 있습니다.
  2. 외부 네트워크 프록시 고려 외부 네트워크의 투명한 및 명시적인 프록시를 고려해야 합니다. 이를 해결하기 위해 TLS를 사용하여 웹소켓 트래픽을 안전하게 터널링할 수 있습니다. 이 방법은 중간 프록시를 우회할 수 있으며, 웹소켓 세션 협상의 성공률을 높이고 연결 시간 초과 간격을 연장하는 데 도움이 됩니다.
  3. 클라이언트 네트워크 고려 클라이언트 네트워크의 라우터, 방화벽, 프록시를 고려해야 합니다. 일부 네트워크에서는 웹소켓 트래픽을 완전히 차단할 수 있으므로 대체 전략을 갖추는 것이 중요합니다.
  4. 인프라 검토 및 사용자 지정 구성 인프라를 검토하고 필요한 경우 사용자 지정 구성을 적용해야 합니다. 예를 들어, Nginx 또는 HAProxy와 같은 서버, 프록시, 부하 분산 장치의 구성을 조정하여 오래 지속되는 연결을 지원할 수 있습니다.

예를 들어, Nginx 를 이용한 구성을 보여드리겠습니다. 이 예제에서는 다음과 같은 구성 요소를 사용합니다.

  1. 업스트림 서버 정의 (Upstream Server Definition)
  2. 로드 밸런싱 알고리즘 (Load Balancing Algorithm)
  3. 프록시 설정 (Proxy Configuration)

먼저 Nginx 설정 파일 (예: /etc/nginx/nginx.conf)에서 업스트림 서버를 정의합니다.

1
2
3
4
5
6
7
http {
    upstream websocket_servers {
        server backend1.example.com:8080;
        server backend2.example.com:8080;
    }
    ...
}

이 코드는 “websocket_servers”라는 이름의 업스트림 서버 그룹을 정의하며, 백엔드 서버 두 개를 포함합니다.

다음으로 로드 밸런싱 알고리즘을 선택합니다. Nginx에서 사용할 수 있는 로드 밸런싱 알고리즘 중 하나는 라운드 로빈입니다. 이 알고리즘은 기본적으로 사용되며, 추가 구성 없이 사용할 수 있습니다. 다른 알고리즘을 사용하려면 아래와 같이 설정합니다.

1
2
3
4
5
6
upstream websocket_servers {
    least_conn; # 최소 연결 알고리즘을 사용하려면 이 코드를 추가
    ip_hash;    # 세션 지속성을 위한 IP 해시 알고리즘을 사용하려면 이 코드를 추가
    server backend1.example.com:8080;
    server backend2.example.com:8080;
}

마지막으로 프록시 설정을 수행합니다. Nginx에서 웹소켓을 프록시하려면 HTTP/1.1 버전을 사용하고 필요한 헤더를 설정해야 합니다. 또한 타임아웃 값을 늘려 안정적인 연결을 보장할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
    listen 80;
    server_name example.com;

    location /websocket {
        proxy_pass http://websocket_servers;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

이 코드는 웹소켓 요청을 “websocket_servers” 업스트림 그룹으로 전달하도록 프록시 설정을 합니다. HTTP/1.1을 사용하고 “Upgrade” 및 “Connection” 헤더를 설정하여 웹소켓 핸드셰이크를 지원합니다. 마지막으로, 읽기 및 전송 타임아웃을 1시간 (3600초)으로 설정하여 오랫동안 연결이 유지되도록 합니다.

이 구성은 웹소켓 서비스에 고가용성과 확장성을 제공하며, 요청을 여러 백엔드 서버 간에 균등하게 분산시킵니다.또한 다음과 같은 이점이 있습니다.

  1. 고가용성: 한 서버에 문제가 발생하더라도 다른 서버가 요청을 처리하여 서비스가 지속됩니다.
  2. 확장성: 백엔드 서버를 추가하거나 제거하여 트래픽 변화에 대응할 수 있습니다.
  3. 성능 최적화: 로드 밸런싱 알고리즘을 사용하여 웹소켓 요청을 최적의 서버로 전달할 수 있습니다.

물론, 웹소켓 인프라 배포를 위한 Nginx 프록시 및 로드 밸런싱 구성은 여러 가지 다른 요소에 따라 변경될 수 있습니다. 예를 들어, 보안을 강화하기 위해 SSL/TLS 설정을 추가할 수 있으며, 웹 애플리케이션 요구 사항에 맞게 다른 로드 밸런싱 알고리즘을 사용할 수 있습니다.

이 예제는 대부분의 인프라에서 오래 지속되는 세션을 처리하기 위해 사용자 지정 구성이 필요한 것을 보여주기 위한 것입니다. 따라서 애플리케이션에서 지속적인 연결을 구현하기 전에 인프라를 검토하는 것이 중요합니다.

장기간 지속되는 유휴 세션은 모든 중간 서버에서 메모리와 소켓 리소스를 소모합니다. 이로 인해 보안, 리소스 및 운영 측면에서 짧은 시간 제한을 설정하는 것이 합리적인 경우가 많습니다. 웹소켓, SSE, HTTP/2와 같이 긴 세션을 사용하는 기술을 도입하면 새로운 운영상의 문제가 발생할 수 있습니다.

요약하면, 웹소켓 인프라 배포를 최적화하려면 다음과 같은 사항을 고려해야 합니다:

  1. 내부 네트워크의 라우터, 로드 밸런서, 프록시를 조정합니다.
  2. 외부 네트워크의 프록시를 고려하고 TLS를 사용하여 웹소켓 트래픽을 안전하게 터널링합니다.
  3. 클라이언트 네트워크의 라우터, 방화벽, 프록시를 고려하고 대체 전략을 준비합니다.
  4. 인프라를 적절하게 검토하고 필요한 경우 사용자 지정 구성을 적용합니다.

이렇게 하면 웹소켓 인프라 배포를 효과적으로 관리하고 성능을 최적화할 수 있습니다.

웹소켓 서비스 성능 향상을 위한 체크리스트

웹소켓 서비스의 성능을 향상시키기 위해 고려해야 할 주요 요소들은 다음과 같습니다:

  1. 최신 웹소켓 라이브러리 사용: 보안과 성능 향상을 확보하기 위해 최신 버전의 웹소켓 라이브러리를 사용하세요. 이렇게 하면 기능 개선 및 버그 수정이 반영된 라이브러리를 활용할 수 있습니다.
  2. 보안 웹소켓 사용: 중간자 공격으로부터 데이터를 보호하기 위해 TLS를 통한 보안 웹소켓(WSS)을 사용하세요.
  3. 서버 성능 최적화: 서버의 메모리, CPU 사용량 및 네트워크 대역폭을 모니터링하고, 필요한 경우 서버 자원을 증가시켜 성능을 향상시키세요.
  4. 프로토콜 최적화: 데이터 전송을 최적화하기 위해 사용되는 프로토콜을 검토하고, 필요한 경우 최적의 프로토콜로 전환하세요.
  5. 커넥션 재사용: 가능한 경우 웹소켓 커넥션을 재사용하여, 커넥션 설정 및 해제 시 발생하는 오버헤드를 최소화하세요.
  6. 메시지 크기 최적화: 데이터를 압축하거나 필요한 데이터만 전송하도록 메시지 구조를 최적화하여 메시지 크기를 최소화하세요.
  7. 스트레스 테스트 및 성능 모니터링: 웹소켓 서비스에 대한 스트레스 테스트를 수행하고, 서비스의 성능을 지속적으로 모니터링하여 문제를 신속하게 파악하고 개선하세요.
  8. 폴리필 성능 주의: 필요한 경우 폴리필 성능에 주의를 기울여 호환성 문제를 해결하면서 성능 저하를 최소화하세요.
  9. 서브프로토콜 협상 활용: 애플리케이션 프로토콜을 결정하기 위해 서브프로토콜 협상을 활용하세요. 이를 통해 클라이언트와 서버 간에 효율적인 통신 방식을 선택할 수 있습니다.
  10. 클라이언트에서 버퍼링된 데이터량 모니터링: 클라이언트에서 버퍼링된 데이터량을 모니터링하여 데이터 전송 속도와 서버 측 처리 속도 간의 균형을 유지하세요. 이를 통해 클라이언트의 메모리 사용량을 최적화하고 애플리케이션 성능을 향상시킬 수 있습니다.
  11. 대용량 애플리케이션 메시지 분할: 대기열 차단을 피하기 위해 대용량 애플리케이션 메시지를 분할하세요. 이렇게 하면 큰 메시지가 전송되는 동안 다른 메시지들이 대기 상태에 빠지는 것을 방지할 수 있습니다.
  12. 이진 페이로드 최적화: 전송 크기를 최소화하기 위해 이진 페이로드를 최적화하세요. 이를 통해 데이터 전송 속도를 높이고, 네트워크 지연을 줄일 수 있습니다.
  13. UTF-8 콘텐츠 압축 고려: 전송 크기를 최소화하기 위해 UTF-8 콘텐츠를 압축할 수 있는지 고려하세요. 이를 통해 텍스트 기반의 메시지 전송 효율을 높일 수 있습니다.
  14. 받은 이진 페이로드에 적절한 이진 타입 설정: 받은 이진 페이로드에 대해 적절한 이진 타입을 설정하여 데이터 처리 속도를 높이세요.

이러한 요소들을 체크리스트로 만들어 성능 향상을 위한 지침으로 활용하면 웹소켓 서비스의 전반적인 성능을 개선할 수 있습니다. 또한 지속적인 모니터링과 최적화를 통해 서비스의 안정성과 확장성을 높일 수 있습니다.

또한 모바일 기기의 경우 배터리 최적화 문제를 신경써야할 수 있습니다. 물론 요즘 디바이스들은 충분한 배터리 용량을 가지고 있지만 그럼에도 이러한 고려는 생각해볼만 합니다.

다음의 타 저자의 게시글들을 확인해 보세요.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

WebSocket 챕터 #1: 서론과 웹소켓 API

백준 랜선 자르기 풀이, 파이썬