자동 주식거래 시스템의 메시지
국내 2천여 종목의 거래체결 데이터는 하루 천만건이 넘습니다. 그야말로 빅데이터의 전형이죠. 자동 주식거래 시스템은 천만건의 빅데이터를 실시간으로 분석해 주가의 방향을 예측해 배팅해야 합니다.
시스템의 성패는 최종적으로 돈을 벌 수 있는 매매전략의 보유여부로 귀결되지만, 당장 밑바닥부터 시스템을 설계하고 구현하는 개발자의 입장에선 과연 천만건의 데이터를 적시에 처리할 수 있느냐가 1차 관문입니다.
메시징 시스템의 종류
시장의 거래체결 데이터, 증권사에 요청할 매매주문 데이터는 사실 단위 시스템간에 주고받는 메시지입니다. 메시지는 자동 주식거래 시스템의 시작과 끝이며, 이를 처리하는 메시징 시스템은 전체 시스템 관점에서 봤을때 척추와 같은 가장 중요한 역할을 담당합니다.
저는 추세추종(Trend Following) 전략을 위주로 시스템을 개발하고 있는데, 나노초(ns)를 다투는 고빈도거래(HFT)까지는 아니더라도 시장의 움직임에 기민한 대응이 필요합니다. 이를 위해 여러대의 컴퓨터에 주가분석 연산량을 분산시켜 처리해야 하며, 최소한의 시간지연(latency)을 위해 로우레벨인 TCP/UDP 소켓 통신부터 시작해 미드레벨 라이브러리인 ZeroMQ, 하이레벨인 RabbitMQ, Apache Kafka 등 메시징큐의 테스트를 진행해 왔습니다.
소켓 통신
UDP에선 일부 패킷이 유실되는 것도 확인했고, TCP 소켓 통신만 하더라도 속도가 엄청나게 빠르다는 것도 느껴봤지만, 과연 어느 세월에 내가 필요한 기능들을 모두 구현해낼 수 있을까 싶어 구현을 중단하고 메시징큐를 찾아보기 시작했습니다.
RabbitMQ
RabbitMQ는 개발언어인 Erlang의 특성에서 기인해 무척 안정적인 것이 특징이며, 매우 강력한 라우팅 기능을 제공합니다. 하지만, 자동 주식거래 시스템에는 메시지의 종류와 처리주체가 많지 않기 때문에 RabbitMQ이 제공하는 강력한 라우팅 기능은 불필요합니다. 그래서 시간지연(latency)이 상대적으로 짧은 ZeroMQ로 넘어왔습니다.
ZeroMQ
ZeroMQ는 메시징큐 시스템이 아닌 라이브러리인데, C++로 작성된 라이브러리를 Python에서 활용할 수 있어 상대적으로 빠른 속도의 통신이 가능했습니다. Clojure에서는 JeroMQ라는 순수 Java로 작성된 라이브러리를 사용할 있는데, 100 byte 단위 메시지 전달에 있어 C++ 라이브러리에 비해서 성능저하는 거의 없는 편입니다(출처).
Apache Kafka
최근 한달 동안은 Apache Kafka에 대한 네권의 서적을 심도있게 읽었는데, Kafka는 실시간 빅데이터 처리의 중요성이 대두되는 시대에 가장 각광받는 기술이다 보니, 값진 자료가 충실하게 준비되어 있었습니다. 특히, Kafka 개발사인 Confluent의 지원으로 O’Reilly 서적들은 PDF 파일이 무료로 제공됩니다.
- 카프카, 데이터 플랫폼의 최강자
- Making Sense of Stream Processing (무료)
- Designing Event-Driven Systems (무료, Kafka를 안써도 읽어볼 것을 추천)
- Kafka: The Definitive Guide (무료)
Kafka는 기존의 메시징큐와는 약간 다릅니다. 기본적으로 엔터프라이즈 서비스 버스(ESB)의 성격을 갖는가 하면, 모든 메시지가 하드디스크에 저장되기 때문에 다수의 메시지 컨슈머 그룹이 시차를 두고 메시지를 가져갈 수 있습니다. 이러한 특성을 활용해 필요하다면 데이터베이스로도 활용할 수 있게 되어있습니다. 때문에 별도의 배치처리 솔루션 없이도 람다 아키텍처구현에 활용할 수 있을 뿐만 아니라, 실시간 처리를 위한 Apache Samza, Storm, Kafka Streams, KSQL을 활용하여 카파 아키텍처를 탄생시키는데 이르렀습니다.
어떤 메시징 시스템을 고를까?
메시지 처리량(Throughput )
하루에 천만건의 거래체결 메시지를 처리해야 한다면, 1초에 처리할 메시지의 수는 500건이 채 되지 않습니다. 순간적으로 주문량이 폭증한다 하더라도 대부분의 메시징 시스템은 초당 십만건 단위의 메시지를 처리할 수 있고요. 따라서 메시징 시스템의 처리량이 전체 시스템의 병목을 일으키진 않습니다. 다만, 메시징 시스템으로 부터 전달받은 주가를 분석하는 시간을 줄이는 것이 관건이므로, 주가분석 연산을 여러대의 노드에 효율적으로 분산시키는 Topology를 구성하는 것이 중요합니다.
지연시간(Latency)
사실 Kafka 도입을 주저하고 있는 이유중에 하나는 ZeroMQ 대비 Latency 증가인데, 2014년 LinkedIn의 벤치마킹 결과를 보면 End-to-End 값이(Kafka에 입력되고 출력되는 시간차이) 평균 3ms 수준입니다. 반면 2010년 ZeroMQ의 벤치마킹 결과를 보면 평균 0.15ms 수준으로, Kafka대비 큰 차이(20배)를 보입니다.
개발 편의성
ZeroMQ는 말그대로 메시징 라이브러리인 만큼, 핵심적인 메시지 전달기능 외에는 모든 것을 개발자의 손에 맡깁니다. 군더더기가 없다고 볼 수도 있고, 한편으로는 개발중에 할일이 많은 대신 생각치 못한 곳에서 문제가 생길 소지는 적다는 이야기입니다.
Kafka는 ZooKeeper을 코디네이터로 활용해 탄력적인 확장성은 물론 고가용성을 제공할 목적으로 개발되었는데, 별도의 서비스로 구동되다 보니 Kafka, ZooKeeper 서버를 운영하는 과정에 겪게될 기술적인 이슈들이 걱정입니다. 이를 극복하지 못하고 Kafka에서 ZeroMQ로 전향한 사례도 무시할 수 없습니다.
결론
여러가지 메시징 시스템을 테스트하고 있지만, 아직까진 과연 어떤 것이 최종 솔루션이 될지는 결정하기 어렵습니다. 현재로선 대부분의 경우에 활용할 수 있는 범용적인 솔루션(Kafka)을 도입하기 보다는, ZeroMQ 등을 활용해 나만의 맞춤 솔루션을 만들어 가는게 어떨까 싶습니다.