Использование ØMQ для
построения распределенных
          систем
              Андрей Охлопков
              Алексей Ермаков
План доклада
• Разработка распределенных систем
• ØMQ: краткий обзор
• Case study: GH ATP
Распределенные системы
Распределенные системы
• Более гибкие в разработке
• Проще масштабировать
• Надежнее
Масштабируемость
Отказоустойчивость
Гетерогенность
Взаимодействие
• HTTP
• TCP/IP
• Message-Oriented Middleware (MOM)
MOM
• Асинхронное взаимодействие через прием
  и отправку сообщений
• Очереди и маршрутизация
Распространенные MOM
•   Amazon SQS
•   MSMQ
•   JMS
•   AMQP (RabbitMQ, Apache Qpid)
ØMQ
• Разработан компанией iMatix (AMQP)
• LGPL
• Поддерживает C/C++, C#, Java, Python, PHP,
  Ruby, Erlang и другие языки
• “Sockets on steroids”
Отличия от других MOM
•   Отсутствие брокера
•   API, похожий на BSD sockets API
•   Произвольный формат сообщений
•   Различные модели взаимодействия
Отличия от обычных сокетов
• N:M взаимодействие
• bind()/connect() могут быть вызваны в
  любом порядке на любой стороне
• Поток сообщений а не байтов
• Автоматическое переподключение
Модели взаимодействия
• Request/reply
• Publish/subscribe
• Pipeline
Request/reply
Request/reply: сервер
import zmq
context = zmq.Context(1)
s = context.socket(zmq.REP)
s.bind("tcp://*:5000")
while True:
  request = s.recv()
  s.send(request.upper())
Request/reply: клиент
import zmq, sys
context = zmq.Context(1)
s = context.socket(zmq.REQ)
s.connect("tcp://localhost:5000")
s.send(sys.argv[1])
print socket.recv(),
Request/reply
$ python server.py &
[1] 79259
$ python client.py foo
FOO
$ python client.py bar
BAR
Request/reply: сервер
import zmq
context = zmq.Context(1)
s = context.socket(zmq.REP)
s.bind("tcp://*:5000")
while True:
  request = s.recv()
  s.send(request.upper())
Request/reply: клиент
import zmq, sys
context = zmq.Context(1)
s = context.socket(zmq.REQ)
s.connect("tcp://localhost:5000")
s.send(sys.argv[1])
print socket.recv(),
Проблема с масштабируемостью
Решение: queue device
Queue = XREQ + XREP + device
Queue = XREQ + XREP + device
import zmq, random, time
context = zmq.Context(1)
xrep = context.socket(zmq.XREP)
xrep.bind("tcp://*:5000")
xreq = context.socket(zmq.XREQ)
xreq.bind("tcp://*:5001")
zmq.device(zmq.QUEUE, xrep, xreq)
Несколько devices в одной сети
Модели взаимодействия
• Request/reply
• Publish/subscribe
• Pipeline
Publish/subscribe
Publish/subscribe: сервер
import zmq, random, time
context = zmq.Context(1)
s = context.socket(zmq.PUB)
s.bind("tcp://*:5000")
Publish/subscribe: сервер
while True:
  for city in ["Moscow", "Murmansk", "St.
Petersburg"]:
    s.send(city, zmq.SNDMORE)
    s.send(str(random.randint(10, 20)))
    time.sleep(2)
Publish/subscribe: клиент
import zmq, sys
context = zmq.Context(1)
s = context.socket(zmq.SUB)
s.connect("tcp://localhost:5000")
Publish/subscribe: клиент
s.setsockopt(zmq.SUBSCRIBE, sys.argv[1])
while True:
  city = socket.recv()
  temp = socket.recv()
  print city + ": " + temp
Publish/subscribe
$ python server.py &
[1] 79569
$ python client.py M
Moscow: 11
Murmansk: 11
Moscow: 13
Murmansk: 17
Модели взаимодействия
• Request/reply
• Publish/subscribe
• Pipeline
Pipeline
Другие возможности
•   PAIR-сокеты
•   Долговременные сокеты
•   Транспорты: in-process, IPC, TCP, PGM
•   Межпоточное взаимодействие
Другие возможности
• Polling
• Альтернативные модели взаимодействия
• Devices (queue, forwarder, streamer и
  собственные)
Что отсутствует
• Транзакции
• Гарантированная доставка сообщений
• Информация о подключениях и контроль
  над ними
Внимание!
• В inproc-сокетах connect() должен быть
  вызван после bind()
• Сокеты привязаны к потокам
• Фильтрация на стороне клиента в PUB/SUB
• assert() в случае ошибок
Заключение
•   Легко использовать
•   Высокая производительность
•   Большой выбор моделей взаимодействия
•   Легко модифицировать существующую
    архитектуру
Case study: GH ATP
Case study: GH ATP
• Автоматизированная торговля ценными
  бумагами
• Большой объем данных (сотни тысяч
  котировок в секунду)
• Жесткие требования к производительности
Задача: получение котировок
• Разные поставщики данных с разными
  протоколами
• Данные используются в нескольких
  продуктах
• Нужен унифицированный API
Требования
• Низкое время отклика (<1 мс)
• Большие объемы данных
• Поддержка нескольких языков (на данный
  момент — Scala и C++)
• Load balancing, fault tolerance
Решение
• Клиент-серверное взаимодействие на
  основе ØMQ
• Google Protocol Buffers для сериализации
  сообщений
Клиент-сервер
Архитектура
Архитектура
• Stateless взаимодействие
• Легко балансировать нагрузку и
  обеспечивать устойчивость
• Высокая производительность
Цифры
•   Тестовые сервер и клиент (Scala), TCP/IP
•   Около 250 000 котировок в секунду
•   Средняя задержка: <1 мс
•   CPU bound (protobuf), ØMQ может дать
    большую производительность
ØMQ — это
• Легкая разработка высонагруженных
  распределенных систем
• Простая модификация и добавление нового
  функционала
• Масштабирование и скорость
•   http://www.zeromq.com
•   http://mongrel2.org
•   Андрей Охлопков <oh@ghcg.com>
•   Алексей Ермаков <ae@ghcg.com>

Использование 0MQ для построения распределёных систем (Андрей Охлопков, Алексей Ермаков)