[네트워크] R소켓 프로토콜

HTTP는 애플리케이션 프로토콜을 위한 간단한 기능을 제공하지만 애플리케이션 시맨틱스(애플리케이션의 동작과 기능에 대한 의미 해석)를 정의하는데 불충분하다. HTTP는 클라이언트-서버 모델 기반의 단순한 상호작용 모델(interaction model)로서, 클라이언트와 서버 간 보다 복잡한 상호작용을 위해서는 추가적인 기능 지원이 필요하며, 애플리케이션은 이를 위해 요청과 응답 처리, 상태 코드 해석, SSE(server-sent events), 플로우 컨트롤(flow control) 등의 기능을 제공해야 한다.

바이너리 프로토콜인 HTTP/2.0의 다중화(multiplexing) 및 바이너리 프레이밍(binary framing)이 리소스 면에서 더 효율적이고 빠른 네트워크 통신을 가능하게 하지만 양방향(bi-directional)이 아니며, HTTP/2.0가 제공하는 서버 푸시(server push) 기능은 클라이언트의 요청 전에 서버가 클라이언트에 응답을 푸시할 수 있도록 하지만 웹 브라우저가 필요로 하는 리소스인 CSS, 자바스크립트, 이미지 등 웹페이지를 구성하는 정적 파일을 캐싱하고 이를 다운로드하는 용도에 그친다. R소켓(RSocket) 프로토콜은 기존 HTTP 프로토콜의 여러 문제들과 한계점을 해결하고 추가적인 기능을 제공하기 위해 등장하였다.

R소켓은 비동기 리액티브 스트림(asynchronous reactive stream) 시맨틱스를 제공하는 애플리케이션 프로토콜이다. 마이크로서비스 통신과 같은 작업에서 비효율적인 HTTP를 보다 오버헤드가 적은 프로토콜로 대체하기 위해 넷플릭스에서 처음 개발하였다. R소켓은 TCP, 에어론(Aeron), 웹소켓을 통해 바이트 스트림 전송 시 사용하기 위한 바이너리 프로토콜이다. 단일 연결 상에서 비동기 메시지 전달을 통해 다음과 같은 기능을 제공한다.

  • HTTP의 단순 요청-응답 모델 뿐만 아니라 스트리밍 응답, 푸시와 같은 상호작용 모델을 지원
  • 애플리케이션 수준의 플로우 컨트롤 시맨틱스 제공
  • 바이너리 프로토콜 사용, 단일 연결에서 다중화 처리
  • 서버의 작업 취소(cancellation) 및 재개(resumability) 기능


R소켓 프로토콜이 제공하는 상호작용 모델은 다음과 같다.

  • 요청-응답 (request-response): 하나의 메시지를 보내고 응답을 받는다.
  • 요청-스트림 (request-stream): 하나의 메시지를 보내고 메시지 스트림을 받는다.
  • 채널 (channel): 메시지 스트림을 양방향으로 보낸다.
  • 실행 후 망각 (fire-and-forget): 단방향 메시지를 보낸다.


초기 연결이 이루어지면 양측이 대칭이 되고 둘 중 하나가 위의 상호 작용 중 하나를 시작할 수 있으므로 클라이언트와 서버의 구분이 사라진다. 따라서 R소켓 프로토콜에서는 상호작용의 참여 측을 요청자(requester)와 응답자(responder)라고 부르며 위의 상호작용을 요청 스트림(request stream) 또는 간단히 요청이라고 한다.

R소켓은 여러 프로그래밍 언어로 구현되어 있다. 자바 라이브러리는 리액티브 스트림 구현체 중 하나인 프로젝트 리액터(Project Reactor)를 기반으로 구축되었으며, TCP 및 웹소켓 용 전송(transport)은 리액터 네티(Reactor Netty)를 기반으로 한다. 리액터 네티는 HTTP(웹소켓 포함), TCP, UDP 프로토콜을 위한 역압을 지원하는 네트워크 엔진이다. 애플리케이션의 리액티브 스트림 퍼블리셔가 보내는 시그널은 네트워크 전반에서 R소켓 프로토콜을 통해 투명하게(transparently) 전파된다.

R소켓은 리액티브한 특성을 갖는 프로토콜이다. 다음 기능을 통해 애플리케이션 프로토콜 수준의 리액티브 기능을 제공한다.

  • 리액티브 시맨틱스: 애플리케이션 내부가 아닌 네트워크 경계를 가로지르는 리액티브 스트림의 시맨틱스는 다음과 같은 특징을 가진다. 요청-스트림 및 채널과 같은 스트리밍 요청의 경우, 역압(backpressure) 신호가 요청자와 응답자 간에 이동하여 요청자가 응답자의 속도를 늦출 수 있다. 이는 네트워크 계층의 혼잡 제어(congestion control)에 대한 의존도와 네트워크 레벨 또는 모든 레벨에서 버퍼링(buffering)의 필요성을 낮춘다.
  • 요청 스로틀링 (request throttling): 이 기능은 주어진 시간 동안 다른 쪽에서 허용되는 총 요청 수를 제한한다. 각 끝에서 보낼 수 있는 LEASE 프레임의 이름을 따서 이 기능을 임대(leasing)라고 한다. 임대는 주기적으로 갱신된다.
  • 세션 재개 (session resumption): 이 기능은 연결이 끊어지는 상황을 대비하여 일부 상태를 유지하기 위해 설계되었다. 상태 관리는 애플리케이션에 대해 투명하므로 애플리케이션은 이를 신경 쓰지 않고도 세션을 재개할 수 있다. 상태 관리는 프로토콜이 자동으로 처리한다. 상태 관리는 가능한 경우 생산자(producer)를 중지하고 필요한 상태의 양을 줄일 수 있는 역압과 함께 잘 작동한다.


R소켓 프로토콜을 사용한 연결 설정 및 통신 과정

  1. 연결 설정: 처음에 클라이언트는 TCP나 웹소켓과 같은 낮은 수준의 스트리밍 전송을 통해 서버에 연결하고 연결에 대한 파라미터를 설정하기 위해 응답자에게 SETUP 프레임을 전송한다. 응답자는 SETUP 프레임을 거부할 수 있지만 일반적으로 전송(요청자) 또는 수신(응답자)되고 난 후 양쪽 모두 요청을 시작할 수 있다. SETUP이 요청 횟수를 제한하기 위해 임대 시맨틱스 사용을 타나내지 않는 한 양쪽 모두 요청을 허용하기 위해 상대방의 LEASE 프레임을 기다려야 한다.
  2. 요청: 연결이 설정되면 양쪽 모두 R소켓이 제공하는 상호작용 모델에 대응되는 REQUEST_RESPONSE, REQUEST_STREAM, REQUEST_CHANNEL 또는 REQUEST_FNF 프레임 중 하나를 통해 요청을 시작할 수 있다. 이러한 각 프레임은 요청자로부터 응답자에게 하나의 메시지를 전달한다. 응답자는 응답 메시지와 함께 PAYLOAD 프레임을 반환할 수 있으며, REQUEST_CHANNEL 프레임의 경우 반대로 요청자가 더 많은 요청 메시지가 포함된 PAYLOAD 프레임을 보낼 수도 있다. 요청에 요청 스트림 및 채널과 같은 메시지 스트림이 포함될 경우 응답자는 요청자의 수요(demand) 시그널을 존중해야 한다. 수요는 여러 개의 메시지로 표현된다. 초기 수요는 프레임에 지정됩니다. 후속 수요는 REQUEST_N 프레임을 통해 신호를 보냅니다. 또한 각 측은 개별 요청이 아닌 연결 전체와 관련된 메타데이터 알림을 METADATA_PUSH 프레임을 통해 전송할 수도 있습니다.


플로우 컨트롤

R소켓은 애플리케이션 수준의 플로우 컨트롤 기능을 제공한다. 따라서 애플리케이션의 논리적인 요구사항과 로직에 의한 네트워크 통신량 제어가 가능하다.

R소켓에서는 수신자가 처리할 수 있는 메시지의 수를 조절하기 위해 REQUEST_N이라는 프레임을 사용한다. 이 프레임은 리액티브 스트림 시맨틱스에서 요청자가 응답자에게 보내는 페이로드의 수와 동일한 의미를 갖는다. 송수신자 간에 공유되는 REQUEST_N 프레임을 통해 수신자가 처리할 수 있는 메시지의 수에 따라 통신량이 제어된다.

이러한 애플리케이션 수준의 플로우 컨트롤은 전송 계층의 바이트 기반 플로우 컨트롤과 독립적이다. 따라서 R소켓을 사용하는 경우 애플리케이션의 처리 속도에 따라 수신자의 버퍼가 가득 차기 전에 메시지의 수를 조절할 수 있다.


gRPC와 R소켓


스프링과 R소켓


프로메테우스 R소켓 프록시

프로메테우스 R소켓 프록시(Prometheus RSocket Proxy)링크는 애플리케이션이 인그레스(ingress)를 오픈할 수 없을 때(인바운드 트래픽은 허용하지 않고 아웃바운드 트래픽만 허용할 때) R소켓을 사용하여 애플리케이션으로부터 메트릭을 가져와 프로메테우스에게 제공하는 역할을 하는 프록시이다. 애플리케이션 관찰가능성(observability) 도구인 마이크로미터 프로젝트에 의해 제공되고 있다.

동작 방식은 다음과 같다.

  1. 애플리케이션은 R소켓 프록시 또는 프록시 클러스터에 TCP R소켓 연결을 생성한다. R소켓 연결이 설정되면 클라이언트와 서버의 구분이 사라지며, 프록시는 각각의 애플리케이션 인스턴스에서 메트릭을 가져올 때 요청자의 역할을 할 수 있다.
  2. 프로메테우스는 애플리케이션 인스턴스가 아닌 프록시의 /metrics/connected/metrics/proxy 엔드포인트를 스크래핑한다.
  3. 프록시가 프로메테우스로부터 스크래핑 요청을 받으면 프록시는 요청/응답 시퀀스를 사용하여 여러 애플리케이션 인스턴스와의 각각의 R소켓 연결을 통해 메트릭을 가져온다. 여러 R소켓 연결의 결과들은 서로 합쳐져서 하나의 응답으로 프로메테우스에 표시된다.
sequenceDiagram
프로메테우스->>R소켓 프록시: 애플리케이션 대신 프록시에게 메트릭 스크래핑 요청
R소켓 프록시->애플리케이션: 애플리케이션으로부터 메트릭 조회


애플리케이션 메트릭 정보가 클라이언트로부터 프록시에게 지속적으로 전송되도록 양방향 연결이 오랜 시간 동안 지속될 필요가 없으며, 이는 클라이언트가 프록시에 자동으로 다시 연결되기 때문이다. 따라서 연결된 클라이언트의 중단에 대한 염려 없이 프록시 클러스터는 자동 스케일 아웃(scale-out)되거나 연결의 균형이 재조정되도록 구성될 수 있다.


TLS와 보안


참고

Comments