• ZeroMQ의 Socket API
ZeroMQ는 새로운 개념을 욱여넣지 않고, 친숙한 소켓 기반 API 이름을 따서 기능을 제공하고 있다. 사용자는 친숙한 이름의 API를 따르되 '분산 소프트웨어를 디자인하고 구현하고 있다.'라는 생각을 갖고 ZeroMQ를 대할 필요가 있다.
ZeroMQ에선 accept() API가 없으며 네트워크 연결은 백그라운드에서 진행되고, 일시적으로 연결이 끊어지더라도 ZeroMQ가 자동으로 다시 연결해준다. 하나 더 기억할 것은 ZeroMQ의 zme_send()의 실제 동작이다. 이 메서드는 메시지를 연결된 소켓으로 바로 전달하는 일을 하지 않고 실제론 메시지를 큐에 넣는 일을 한다. (사용자는 인지하지 않아도 되는) I/O 담당 쓰레드가 비동기적으로 큐에 쌓인 메시지를 실제 연결된 소켓으로 전달하는 일을 할 것이다.
대부분의 유스케이스는 TCP 방식으로 커버가 된다. ZeroMQ에서는 TCP를 'disconnected TCP' 전송 방식이라고도 표현을 하는데 이는 엔드 포인트가 연결이 되지 않아도 작동하기 때문에 이름지어진 표현이다. 클라이언트는 엔드포인트에 바인드된 서버가 없더라도 별다른 오류 없이 connect 작업을 할 수 있는 것은 ZeroMQ가 갖는 특징이다.
inproc이라는 방식의 연결도 제공하는데 이는 tcp, ipc 전송 방식보다 빠르게 작동한다. 다만 클라이언트 연결전에 반드시 서버가 실행되어 bind()를 하고 있어야 하는 제약 조건이 걸린다. (이후 버전에서 해결할 예정이라고..)
ZeroMQ에서 context를 만들면 하나의 I/O 쓰레드가 백그라운드에서 작업을 시작하게 된다. 일반적인 요구조건에선 하나의 I/O 쓰레드만 가지고 모든 작업을 처리할 수 있다고 한다. I/O 쓰레드의 개수를 늘리고 싶은 경우엔 zmq_ctx_set() 메서드의 ZMQ_IO_THREADS 옵션을 조정하면 된다.
• ZeroMQ가 내부적으로 해주는 일
ZeroMQ가 하는 일을 요약하면,
1) 노드에 빠르고 효율적으로 메시지를 전송해주고,
2) 자동으로 다시 연결을 해주며
3) 메시지 큐잉을 해준다.
4) 메시지 큐는 프로세스가 메모리 부족 현상을 겪지 않게끔 적절한 크기에서 제한되며
5) 소켓 오류를 처리해준다.
6) 마지막으로 노드간 통신에서 데드락 같은 현상을 겪지 않게 보장해준다.
• ZeroMQ가 제공하는 소켓 페어
목록 중에선 거의 상위 3개 목록 중 하나를 쓰게될 것 같다. 아래의 패턴 외 다른 조합을 쓰는 경우엔 ZeroMQ가 올바른 동작을 보장해주지 않는다.
- PUB - SUB : 디커플링된 형태의 데이터 분산 패턴.
- REQ - REP
- PUSH - PULL : fan-out / fan-in의 병렬 작업 분산 및 수집 패턴에 적합.
- REQ - ROUTER
- DEALER - REP
- DEALER - ROUTER
- DEALER - DEALER
- ROUTER - ROUTER
- PAIR - PAIR
PUB - SUB과 REP - REQ 같은걸 조합해서 쓸 수도 있는데 예를 들어 모든 subscriber가 연결된 이후에 publish할 수 있도록 애플리케이션을 디자인 해야 하는 경우에 유용하다. 간단한 시나리오는
1) Publisher가 PUB 소켓을 열고 "Hello" 메시지를 발행한다.
2) Subscriber는 SUB 소켓을 열고 "Hello" 메시지에 응답한다. (REQ/REP 페어를 통해)
3) Publisher는 Subscriber의 응답을 확인한 뒤 실제 데이터 전송을 시작한다.