ZeroMQ
네트워크 프로토콜은 구현 위치에 따라 커널 계층과 애플리케이션 계층으로 구분할 수 있으며, 각 위치에는 고유한 장단점이 있다. 초기 인터넷 프로토콜은 주로 운영체제 커널 내부에 구현되었으나, 현대 프로토콜은 점차 애플리케이션 계층으로 이동하는 추세를 보인다.
위치별 특성:
- 커널 계층: 데이터 처리 효율성과 메모리 사용 최적화 측면에서 우수하나, 개발과 업데이트가 복잡함
- 애플리케이션 계층: 구현과 확장이 용이하고 유연성이 높으나, 상대적으로 오버헤드가 발생할 수 있음
기존 TCP/UDP/IP와 같은 저수준 프로토콜에서는 소켓 API가 커널과 응용 프로그램 사이의 중간 인터페이스 역할을 담당한다. POSIX 호환 시스템에서는 소켓이 파일 디스크립터로 추상화되어, 파일 시스템의 일부로 취급된다.
ZeroMQ(ØMQ, 0MQ, 또는 zmq)는 2007년 iMatix에서 개발한 고성능 비동기 메시징 라이브러리로, 애플리케이션 계층에서 동작한다. C++로 개발되었으며 28개 이상의 프로그래밍 언어를 지원하고, 다양한 플랫폼에서 실행 가능하다. 소프트웨어는 LGPLv3+ 라이센스 하에 배포된다.
ZeroMQ의 'ZERO'는 다음의 핵심 철학을 반영한다:
- 브로커리스: 중앙 메시지 브로커 없이 직접 통신
- 제로 지연: 최소화된 처리 지연 시간
- 무료: 비용 부담 없는 오픈소스
- 제로 관리: 복잡한 설정이나 유지보수 작업 최소화
이 라이브러리는 분산 시스템과 병렬 프로그래밍을 위해 설계되었으며, TCP/UDP부터 프로세스 내 통신, 웹소켓에 이르기까지 다양한 전송 프로토콜을 지원한다.
ZeroMQ는 다음과 같은 핵심 메시징 패턴을 제공한다:
- 요청-응답(Request-Reply): 클라이언트와 서비스 간의 양방향 통신을 위한 패턴으로, 원격 프로시저 호출이나 작업 분배에 활용된다. (소켓 타입: REQ, REP, DEALER, ROUTER)
- 발행-구독(Publish-Subscribe): 데이터 발행자와 구독자 간의 일방향 데이터 분배 모델을 제공한다. 구독자는 관심 있는 데이터만 수신할 수 있다. (소켓 타입: PUB, SUB, XPUB, XSUB)
- 파이프라인(Pipeline): 복수의 노드 간 데이터 흐름을 관리하는 패턴으로, 병렬 작업 처리와 데이터 수집에 적합하다. 다단계 처리와 피드백 루프를 구성할 수 있다. (소켓 타입: PUSH, PULL)
- 배타적 쌍(Exclusive Pair): 두 엔드포인트 간의 독점적 통신 채널을 설정하는 패턴으로, 주로 멀티스레딩 환경에서 활용된다.
기존 소켓과 ZeroMQ 소켓 간에는 근본적인 차이가 있다:
- 전통적 소켓: 바이트 스트림(TCP) 또는 데이터그램(UDP) 기반의 동기식 통신을 제공하며, 주로 1:1 또는 N:1 연결 구조에 최적화되어 있다.
- ZeroMQ 소켓: 비동기 메시지 큐 메커니즘을 추상화하여 제공하며, 이산적 메시지 처리와 N 관계 구성이 가능하다. 소켓 유형에 따라 메시지 처리 방식과 큐 동작이 달라진다.
ZeroMQ의 비동기 소켓은 물리적 연결 관리, 재연결 처리, 메시지 전달 타이밍 등을 자동으로 처리하여 개발자의 부담을 줄인다. 또한 수신측이 일시적으로 메시지를 처리할 수 없는 경우, 내부 큐에 저장하여 신뢰성을 보장한다.
ZeroMQ에서 바인드(bind)와 연결(connect)은 유연하게 적용할 수 있다. 전통적으로 서버는 바인드, 클라이언트는 연결 방식을 사용하지만, ZeroMQ에서는 이러한 구분이 엄격하지 않다.
ZeroMQ 소켓이 연결을 관리하는 방식:
- 소켓이 피어에 연결될 때마다 해당 연결을 위한 전용 메시지 큐가 생성된다.
- 바인드된 소켓은 미래의 연결을 대비하여 대기하며, 각 연결이 수립될 때 개별 큐가 동적으로 생성된다.
- 연결을 시작하는 소켓은 명시적인 피어 대상이 있으므로, 즉시 해당 피어를 위한 큐를 생성할 수 있다.
바인드와 연결 선택에 관한 실용적 가이드라인:
- 네트워크 토폴로지에서 가장 안정적이고 지속적인 구성 요소는 바인드를 사용하는 것이 적합하다.
- 자주 변경되거나 동적으로 생성/제거되는 구성 요소는 연결 방식을 사용하는 것이 효율적이다.
- 요청-응답 패턴에서는 서비스 제공자가 바인드, 클라이언트가 연결하는 것이 일반적이다.
- P2P 네트워크와 같이 명확한 역할 구분이 어려운 경우, 중앙 디바이스를 통한 연결 방식을 고려할 수 있다.
RabbitMQ
메시지 브로커는 애플리케이션 간의 통신을 중개하는 중요한 미들웨어로, 비동기 메시징을 통해 시스템의 확장성과 탄력성을 향상시킨다. RabbitMQ는 오픈 소스 메시지 브로커로, AMQP(Advanced Message Queuing Protocol)를 구현한 중앙 집중형 브로커 기반 메시징 시스템이다. 이는 분산 시스템의 다양한 구성 요소 간에 메시지를 전달하는 중개자 역할을 한다.
RabbitMQ 서비스의 워크플로우는 다음과 같은 핵심 구성 요소를 가진다:
- Producer (생산자): 메시지를 생성하여 RabbitMQ에 보내는 애플리케이션이다.
- Exchange (교환기): 생산자로부터 메시지를 받아 특정 규칙에 따라 큐에 분배하는 라우터 역할을 한다. 교환기는 다양한 라우팅 전략(direct, topic, fanout, headers)을 지원한다.
- Queue (큐): 메시지가 소비될 때까지 저장되는 버퍼이다. 큐는 메시지 순서를 보존하고 FIFO(First In, First Out) 방식으로 작동한다.
- Consumer (소비자): 큐에서 메시지를 꺼내어 처리하는 애플리케이션이다.
RabbitMQ 서비스의 한 예로, 위 그림에서 보여지는 PDF 생성 워크플로우의 메시지 흐름은 다음과 같다:
- 사용자가 웹 애플리케이션에서 "Create PDF" 버튼을 클릭한다.
- 웹 애플리케이션(Producer)이 사용자 정보와 함께 PDF 생성 요청 메시지를 생성한다.
- 메시지는 RabbitMQ의 Exchange로 전송되고, 정의된 라우팅 규칙에 따라 적절한 큐로 라우팅된다.
- PDF Creator Worker(Consumer)가 메시지 큐를 지속적으로 모니터링하다가, 새로운 메시지가 도착하면 이를 꺼내어 처리한다.
위 그림은 더 복잡한 메시지 라우팅 패턴을 보여준다:
- 여러 교환기(x1, x2)가 서로 연결되어 있다.
- 다수의 큐(q1, q2, q3)가 다른 라우팅 패턴으로 메시지를 받는다.
- 여러 소비자(c1, c2, c3)가 각각의 큐에서 메시지를 독립적으로 처리한다.
이러한 구조는 유연한 메시지 라우팅과 다양한 처리 시나리오를 지원한다.
RabbitMQ와 함께 자주 언급되는 또 다른 메시징 시스템으로 Apache Kafka가 있다. 두 시스템은 분산 메시징을 제공하지만, 아키텍처와 사용 사례가 다르다:
- RabbitMQ: 다양한 메시지 라우팅 패턴을 지원하며, 복잡한 라우팅과 낮은 지연 시간이 필요한 경우에 적합하다. 중앙 집중형 브로커 모델을 사용한다.
- Kafka: 대용량 로그 처리와 데이터 스트리밍에 최적화되어 있다. 높은 처리량과 내구성이 필요한 빅데이터 시나리오에 적합하다. 분산 커밋 로그 모델을 사용한다.
메시지 브로커를 사용하는 분산 컴퓨팅 시스템의 주요 장점은 다음과 같다:
- 비동기 통신: 시스템 구성 요소는 동시에 활성화될 필요가 없으며, 요청-응답 모델보다 느슨하게 결합된다.
- 부하 분산: 메시지 큐를 통해 여러 소비자 간에 작업을 분배할 수 있다.
- 확장성: 생산자와 소비자를 독립적으로 확장할 수 있다.
- 내구성: 메시지는 처리될 때까지 지속적으로 저장된다.
- 유연성: 시스템의 다른 부분에 영향을 주지 않고 새로운 서비스를 추가하거나 기존 서비스를 수정할 수 있다.
메시지 브로커는 현대적인 마이크로서비스 아키텍처, 이벤트 기반 시스템, 그리고 분산 컴퓨팅 환경에서 핵심적인 역할을 한다.
Request-Reply 패턴
ZeroMQ(ZMQ)는 다양한 메시징 패턴을 제공하는 비동기 메시징 라이브러리로, 그 중 Request-Reply 패턴은 가장 기본적이면서도 널리 사용되는 패턴 중 하나이다. 이 패턴은 클라이언트-서버 모델의 구현에 특히 유용하다.
Request-Reply 패턴은 다음과 같은 기본 흐름을 가진다:
- 클라이언트(REQ 소켓)가 서버에 요청(Request)을 보낸다.
- 서버(REP 소켓)가 요청을 받고 처리한다.
- 서버는 클라이언트에 응답(Reply)을 보낸다.
- 클라이언트는 응답을 받고 처리한다.
위 그림은 이 패턴의 기본 구조를 시각적으로 보여준다. 클라이언트는 REQ 소켓을 사용하여 "Hello" 메시지를 서버에 보내고, 서버는 REP 소켓을 사용하여 "World" 메시지로 응답한다.
REQ-REP 소켓 쌍은 동기식(Synchronous) 통신 모델을 따른다:
- 엄격한 시퀀스: REQ 소켓은 반드시 send() 후 recv()를 호출해야 하며, REP 소켓은 반드시 recv() 후 send()를 호출해야 한다. 이 순서를 위반하면 오류가 발생한다.
- 통신 지원: 하나의 REP 소켓은 여러 REQ 소켓의 요청을 처리할 수 있다. 서버는 수천 개의 클라이언트 요청도 효율적으로 처리할 수 있다.
- 메시지 프레임 관리: ZMQ는 내부적으로 메시지 프레임을 관리하여 요청과 응답이 올바르게 짝지어지도록 보장한다.
서버는 REP 소켓을 생성하고 5555 포트에 바인딩한다. 그런 다음 무한 루프에서 클라이언트의 요청을 받고, 1초간 '작업'을 시뮬레이션한 후, "World"라는 응답을 돌려보낸다.
클라이언트는 REQ 소켓을 생성하고 서버(localhost:5555)에 연결한다. 그런 다음 10번의 요청을 순차적으로 보내고, 각 요청마다 서버의 응답을 기다린다.
Request-Reply 패턴의 장점은 다음과 같다:
- 간단한 구현: 클라이언트-서버 모델을 구현하기 위한 코드가 간결하다.
- 자동 라우팅: ZMQ는 내부적으로 메시지 라우팅을 처리하여 올바른 클라이언트에게 응답이 돌아가도록 보장한다.
- 확장성: 하나의 서버가 여러 클라이언트의 요청을 처리할 수 있다.
- 내장된 로드 밸런싱: 여러 서버 인스턴스가 있는 경우, ZMQ는 자동으로 부하 분산을 처리한다.
또한, Request-Reply 패턴을 사용할 때 고려해야 할 상황은 다음과 같다:
- 동기식 통신: REQ-REP 패턴은 기본적으로 동기식이므로, 클라이언트는 응답을 받을 때까지 차단된다.
- 엄격한 시퀀스: 메시지 교환 순서를 엄격하게 지켜야 한다.
- 타임아웃 설정: 실제 애플리케이션에서는 recv() 호출에 타임아웃을 설정하는 것이 좋다.
Request-Reply 패턴은 간단한 RPC(Remote Procedure Call) 시나리오나 요청-응답 기반의 서비스에 적합하다
Publish-Subscribe 패턴
Publish-Subscribe(발행-구독) 패턴은 데이터 배포에 특화된 일방향 통신 모델이다. 이 패턴은 여러 클라이언트에게 효율적으로 정보를 전파해야 하는 시스템에 적합하다.
Publish-Subscribe 패턴은 다음과 같은 구조로 동작한다:
- 발행자(Publisher)는 메시지를 생성하고 특정 주제(topic)로 발행한다.
- 구독자(Subscriber)는 관심 있는 주제를 구독하고 해당 주제의 메시지만 수신한다.
- 발행자와 구독자 사이의 통신은 일방향이며, 발행자는 구독자의 존재 여부와 관계없이 메시지를 발행한다.
위 그림은 이 패턴의 기본 구조를 보여준다. 발행자(PUB 소켓)는 바인드(bind)하고 여러 구독자(SUB 소켓)는 연결(connect)하여 업데이트를 수신한다.
PUB-SUB 소켓 쌍은 비동기식(Asynchronous) 일방향 통신 모델을 따른다:
- 단방향 통신: PUB 소켓은 오직 메시지를 발행(send)만 할 수 있고, SUB 소켓은 오직 메시지를 구독(recv)만 할 수 있다. 역방향 시도는 오류를 발생시킨다.
- 메시지 필터링: 구독자는 관심 있는 주제(topic)를 지정하여 해당 주제와 관련된 메시지만 수신할 수 있다. 주제는 일반적으로 메시지 접두사로 구현한다.
- 느슨한 결합: 발행자는 구독자의 존재를 알지 못하며, 구독자의 수나 상태에 관계없이 메시지를 계속 발행할 수 있다.
구독자는 SUB 소켓을 생성하고 발행자에 연결한 후, 특정 우편번호에 대한 필터를 설정한다. 핵심은socket.setsockopt_string(zmq.SUBSCRIBE, zip_filter) 호출로, 이는 특정 우편번호로 시작하는 메시지만 수신하도록 한다.
Publish-Subscribe 패턴의 장점은 다음과 같다:
- 확장성: 새 구독자를 추가하더라도 발행자 코드를 수정할 필요가 없다.
- 필터링 메커니즘: 구독자는 관심 있는 메시지만 수신할 수 있다.
- 비동기 통신: 발행자와 구독자는 독립적으로 동작한다.
- 통신: 하나의 발행자가 여러 구독자에게 효율적으로 메시지를 전파할 수 있다.
이 패턴은 실시간 데이터 피드, 뉴스 업데이트, 모니터링 시스템, 로그 배포 등과 같은 시나리오에 이상적이다.
Pipeline 패턴과 복합 패턴
Pipeline 패턴은 작업 분배와 결과 수집에 특화된 통신 모델이다. 더 복잡한 시스템에서는 여러 패턴을 조합하여 사용하는 복합 패턴도 널리 활용된다.
Pipeline 패턴은 다음과 같은 구조로 동작한다:
- 작업 분배자(Ventilator)는 PUSH 소켓을 사용하여 작업을 여러 작업자(Worker)에게 분배한다.
- 작업자들은 PULL 소켓으로 작업을 받아 처리하고, 결과를 PUSH 소켓을 통해 결과 수집기(Sink)로 전송한다.
- 결과 수집기는 PULL 소켓으로 모든 작업자로부터 결과를 수집한다.
위 그림의 왼쪽 부분은 여러 PUSH 소켓이 하나의 PULL 소켓으로 작업을 전송하는 모습을 보여준다. 오른쪽 부분은 전형적인 Pipeline 패턴의 구조를 보여주는데, Ventilator에서 Workers로 작업이 분배되고, Workers에서 Sink로 결과가 수집된다.
PUSH-PULL 소켓 쌍은 다음과 같은 특성을 가진다:
- Fair Queuing: ZeroMQ는 기본적으로 라운드 로빈 방식으로 메시지를 분배하여 작업이 작업자들에게 균등하게 분산된다.
- Load Balancing: 작업자들이 자신의 처리 속도에 맞게 작업을 가져가므로, 자연스러운 부하 분산이 이루어진다.
- 단방향 데이터 흐름: PUSH 소켓은 보내기만 할 수 있고, PULL 소켓은 받기만 할 수 있다.
위 그림은 PUB-SUB 패턴과 PUSH-PULL 패턴을 결합한 복합 패턴을 보여준다. 이러한 구조는 다음과 같이 작동한다:
- 서버(Server):
- PUB 소켓: 모든 클라이언트에게 업데이트를 브로드캐스트한다.
- ROUTER 소켓: 클라이언트로부터의 특정 요청을 처리한다.
- PULL 소켓: 클라이언트로부터 상태 업데이트를 수신한다.
- 클라이언트(Client):
- SUB 소켓: 서버의 브로드캐스트 업데이트를 구독한다.
- DEALER 소켓: 서버에 상태 요청을 보낸다.
- PUSH 소켓: 자신의 상태 업데이트를 서버에 보고한다.
이러한 복합 패턴은 양방향 통신과 브로드캐스팅을 모두 가능하게 하여, 분산 시스템에서 더 유연한 통신을 지원한다.
다음은 ZeroMQ의 Python 바인딩을 사용한 PUSH-PULL과 PUB-SUB 패턴을 조합한 예제 코드이다.
서버는 PUB 소켓과 PULL 소켓을 설정한다. PULL 소켓을 통해 클라이언트로부터 메시지를 수집하고, 이를 PUB 소켓을 통해 모든 클라이언트에게 전파한다.
클라이언트는 SUB 소켓과 PUSH 소켓을 설정한다. SUB 소켓을 통해 서버의 브로드캐스트를 수신하고, PUSH 소켓을 통해 자신의 상태 정보를 서버에 전송한다.
복합 패턴을 활용한 애플리케이션은 다음과 같이 동작한다:
- 서버는 PUB와 PULL 기능을 함께 구동하여, 클라이언트들이 보고하는 상태 정보를 PULL 소켓을 통해 수집한다.
- 수집된 정보는 다시 PUB 소켓을 통해 모든 클라이언트에게 브로드캐스트된다.
- 클라이언트는 SUB와 PUSH 기능을 사용하여, 주기적으로 자신의 상태 정보를 서버에 PUSH하고, 서버가 발행하는 전체 클라이언트의 상태 정보를 SUB 소켓을 통해 수신한다.
이러한 구조는 분산 모니터링 시스템, 채팅 애플리케이션, 실시간 협업 도구 등에서 효과적으로 활용될 수 있다.
Pipeline 패턴의 장점은 다음과 같다:
- 확장성: 작업자(Worker)를 추가하거나 제거하여 시스템의 처리 용량을 쉽게 조정할 수 있다.
- 부하 분산: 자동으로 작업이 모든 작업자에게 분산되므로, 시스템 자원을 효율적으로 활용할 수 있다.
- 병렬 처리: 여러 작업자가 동시에 작업을 처리할 수 있어 전체 처리 시간이 단축된다.
- 견고성: 작업자가 실패하더라도 다른 작업자들이 계속 작업을 처리할 수 있다.
Pipeline 패턴은 데이터 처리 파이프라인, 분산 컴퓨팅, 작업 큐 시스템 등에 이상적이며, 다른 ZeroMQ 패턴과 결합하여 더 복잡한 메시징 토폴로지를 구성할 수 있다.
Dealer-Router 패턴
Dealer-Router 패턴은 비동기 통신과 확장성이 요구되는 시스템에 특화된 솔루션이다.
Dealer-Router 패턴은 기존 Request-Reply 패턴의 비동기 버전으로, 다음과 같은 특징을 가진다:
- 비동기 통신: 메시지를 보낸 후 응답을 기다리지 않고도 계속해서 새로운 메시지를 전송할 수 있다.
- 메시지 라우팅: Router 소켓은 들어오는 모든 메시지의 출처를 자동으로 추적하여 응답을 올바른 대상에게 전달한다.
- N 통신: 여러 클라이언트와 여러 워커 간의 복잡한 통신 토폴로지를 구성할 수 있다.
이 패턴의 구조는 일반적으로 다음과 같이 구성된다:
- 클라이언트는 Dealer 소켓을 사용하여 서버에 연결한다.
- 서버는 Router 소켓으로 클라이언트 연결을 수신하고, Dealer 소켓으로 워커들과 통신한다.
- 워커들은 Dealer 소켓을 사용하여 서버의 백엔드에 연결한다.
Dealer-Router 패턴의 실제 구현은 다음과 같은 요소로 구성된다:

서버는 외부 클라이언트와 통신하는 프론트엔드(ROUTER) 소켓과 내부 워커와 통신하는 백엔드(DEALER) 소켓을 가진다.
서버는 zmq.proxy() 함수를 사용하여 클라이언트의 요청을 워커에게 분배하고, 워커의 응답을 적절한 클라이언트에게 라우팅한다.
워커는 서버의 내부 엔드포인트에 연결하여 작업을 받고, 결과를 반환한다. recv_multipart()와 send_multipart()를 사용하여 메시지와 함께 송신자 ID를 처리한다.
inproc은 ZeroMQ의 프로세스 내(in-process) 통신 프로토콜이다.
클라이언트는 고유 ID를 가진 Dealer 소켓을 사용하여 서버에 비동기적으로 요청을 보내고, 폴링을 통해 응답을 확인한다. 폴링(Polling)은 비차단(non-blocking) 방식으로 소켓의 이벤트를 감지하는 기법이다.
Dealer-Router 패턴은 다음과 같은 시나리오에 특히 적합하다:
- 부하 분산 시스템: 들어오는 요청을 여러 처리 노드에 효율적으로 분산시킨다.
- 마이크로서비스 아키텍처: 서비스 간 비동기 통신을 구현하여 시스템 전체의 응답성을 향상시킨다.
- 실시간 데이터 처리: 스트리밍 데이터를 여러 워커에 분배하여 병렬로 처리한다.
- 분산 계산: 복잡한 계산 작업을 여러 노드에 분산하여 처리한다.
Dealer-Router 패턴의 장점은 다음과 같다.
- 높은 확장성: 클라이언트와 워커를 필요에 따라 추가/제거할 수 있다.
- 유연한 메시징: 동기식 패턴의 제약 없이 자유롭게 메시지를 교환할 수 있다.
- 효율적인 리소스 활용: 비동기 처리로 시스템 자원을 최대한 활용할 수 있다.
HTTP Appendix
HTTP/2와 HTTP/3은 웹 통신의 속도와 효율성을 획기적으로 향상시킨 프로토콜이다. 구글은 이 프로토콜들을 크롬 브라우저에 선제적으로 도입했다.
HTTP/2는 2015년에 표준화되었으며, 구글의 SPDY 프로토콜에서 발전한 기술이다. 이 프로토콜은 멀티플렉싱, 헤더 압축, 서버 푸시 등의 기능을 통해 웹 페이지 로딩 시간을 단축시켰다.
HTTP/3는 TCP 대신 구글이 개발한 QUIC(Quick UDP Internet Connections) 프로토콜을 기반으로 한다. QUIC은 UDP를 사용해 연결 설정 시간을 줄이고, 패킷 손실에 더 효율적으로 대응하며, 네트워크 전환 시에도 연결을 유지한다.
구글이 이러한 프로토콜을 크롬 브라우저에 빠르게 도입한 이유는 다음과 같다:
- 웹 브라우징 속도 향상을 통한 사용자 경험 개선이다.
- 자사의 웹 서비스 성능 최적화를 위한 전략이다.
- 웹 기술 발전 방향에 대한 영향력 확보를 위한 수단이다.
- 크롬 브라우저의 시장 경쟁력을 강화하기 위한 방안이다.
이러한 기술적 선도는 구글이 웹 기술 생태계에서 주도적인 위치를 차지하는 데 중요한 역할을 했다.
'학교공부 > 풀스택 서비스 네트워킹' 카테고리의 다른 글
[풀스택 서비스 네트워킹] WebRTC (4) | 2025.06.09 |
---|---|
[풀스택 서비스 네트워킹] gRPC (0) | 2025.06.09 |
[풀스택 서비스 네트워킹] Socket (1) | 2025.03.30 |
[풀스택 서비스 네트워킹] OSI Architecture L1,L2,L3 (1) | 2025.03.30 |
[풀스택 서비스 네트워킹] OSI Architecture Overall (0) | 2025.03.14 |