
Всем привет! Меня зовут Николай Висков, я лидер направления разработки протокола доступа Биржи, работаю в блоке развития торгово-клиринговых систем, занимаюсь разработкой высоконагруженных систем.
Лирическое отступление
Существует два фундаментальных подхода к доставке данных: уникаст — передача «точка-точка», и мультикаст — передача «один-ко-многим». Подавляющее большинство данных в локальных и глобальных сетях ходит по TCP, уникасту. У большинства программистов сложилась четкая ассоциация: если нужно устроить сетевое взаимодействие, то используем TCP. Но в финансовой сфере на этот счёт мы привыкли думать по-другому. Если требуется построить систему с минимальным временем отклика, но при этом с высокой пропускной способностью и надежной доставкой, то мультикаст годится лучше.
Понятно почему так получилось. Мультикаст невозможно использовать через интернет. Он сложен в настройке и опасен образованием петель, которые могут положить всю сеть. Но если не бояться сложности, то преимущества могут быть значительными. Когда вы набираете сообщение в корпоративном мессенджере в частной сети, оно доходит до всех реципиентов по мультикасту или есть демон, который будет копировать сообщение по сокетам для всех участников чата? А при проведении корпоративного таунхолла видео докладчика будет идти через единую точку отказа в виде демона или использовать мультикаст и иметь неограниченное количество слушателей?
Может показаться, что мультикаст должен работать медленнее уникаста. Транслировать несколько сообщений вместо одного, это, наверное, не быстро? Однако это не так. На физическом уровне, поток фотонов, как и электрический сигнал, могут бесплатно «раздваиваться». Когда в солнечный день на стене у нас висит пятно солнечного зайчика, мы можем поставить призму и сделать два из одного. Или три… При этом распространяться «зайцы» будут по-прежнему: (почти) со скоростью света.


Где мультикаст используется на Бирже: 4 способа
Итак, зачем мультикаст нужен нам на Бирже? Попробую описать несколько случаев, где мы его используем:
1. Самое очевидное — это распространение публичной рыночной информации.
Биржа раздает такие данные посредством IP/UDP мультикаст по протоколам SIMBA (от SIMpleBinAry) и FAST (от FIX Adapted for STreaming). Это биржевой стандарт, так делают все и это наиболее честный способ уведомлять о заявках и сделках на бирже. Благодаря мультикасту все участники в зоне колокации получают данные одновременно. Как правило, это два фида обычного IP/UDP мультикаста с возможными потерями и переупорядочиванием.
2. Быстрое и надежное резервирование сервисов.
Предположим, в упрощенной схеме ТКС есть несколько компонентов: Шлюз → Диспатчер → Риски → Матчинг → Снова Шлюз. Если компонент детерминирован, то надежный мультикаст в такой схеме позволяет резервировать сервис просто добавив его копию и сделав дедупликацию исходящих сообщений.
3. Мониторинг потоков данных.
Биржа — это среда (очень) строгой отчетности. Поэтому хорошей практикой считается захватывать весь трафик на входе и выходе каждого из компонентов ТКС. Трафик захватывается на уровне сети в виде Packet Capture там, где это возможно. Также он подслушивается специальными сервисами, которые подписываются на мультикаст группы и сохраняют все данные в виде текстовых или бинарных файлов.
4. Раздача ExecLog микросервисам.
Хорошим подходом к архитектуре считается разнести бизнес-логику на некоторое количество слабо связанных сервисов. На бирже основным источником данных для таких сервисов является поток заявок и сделок, распространяемый из ядра торговой системы. Такой поток содержит всю необходимую информацию для генерации всех сообщений ExecutionReport по стандарту протокола FIX и потому называется ExecLog. Таким образом, мы имеем один высоконагруженный поток данных и много потребителей для него. Мультикаст в таком случае – естественный выбор, очень хорошо ложится в нашу схему. При этом от источника до получателя будет всего один транзитный участок (hop). Никакой мессаджинг с брокером сообщений не сможет предложить более короткое время отклика.

Я вижу у способа распространения данных один-ко-многим без потерь пакетов такое количество преимуществ, что мне кажется странным то, что надежный мультикаст так слабо распространен за пределами финансовой сферы. На собеседованиях в крупные IT компании на солидную должность, как правило, просят пройти этап по архитектуре ПО. Там наверняка предложат спроектировать архитектуру надежного сервиса, который будет обрабатывать запросы клиентов. Скорее всего, как правильный ответ примут архитектуру, которая будет включать использование очереди сообщений с брокером: Kafka, RabbitMQ или SQS. Вспоминают ли в таких случаях про архитектуру с использованием раздачи сообщений по мультикасту? Я сомневаюсь.
Нигде, ни на HighLoad, ни здесь на Хабре, я не сталкивался с тем, чтобы за пределами финансовой сферы решили использовать месседжинг без брокера, на основе мультикаста.
Нужен ещё один способ!
Когда мы начали разрабатывать новую технологическую платформу для Биржи, нам потребовался еще один надежный и быстрый способ отправлять сообщения «один-ко-многим».
От транспорта мы ожидали получить:
1. Низкое и что еще важнее предсказуемое, ровное время отклика.
2. Надежную доставку сообщений с корректной обработкой различных сбоев оборудования и программного обеспечения.
3. Возможность отправить сообщение один-ко-многим, не переплачивая за каждого дополнительного слушателя.
При этом внутренняя сеть Биржи построена на основе RDMA сети, а значит нам нужна поддержка RDMA UD на нативном уровне. RDMA – это прямой доступ к оперативке и тотальная минимизация задержек, что для обработки транзакций на бирже необходимо как воздух.
Первым делом мы провели RFI, тендер на разработку ПО, но желающих сделать нам такой софт не нашлось вообще. Слишком сложно?
Тогда мы решили поэкспериментировать с OSS. Так как нам требовался надежный мультикаст, то мы смотрели на реализации протокола PGM.
Мне особенно был близок PGM, так как когда-то именно благодаря статье о этом протоколе я получил свой инвайт на Хабр.
Мы нашли несколько подходящих OSS решений, а после тестирования и сравнения остановились на OpenPGM. В нем нас устраивало все, кроме отсутствия поддержки RDMA. Но добавить RDMA, как оказалось, задача решаемая.

PGM был спроектирован под IP-мультикаст, это прямо прописано спеке. То есть, если мы любым способом настроим PGM поверх RDMA UD, мы отступим от канонов, заложенных в протоколе. В спецификации PGM есть пункты, которые плохо совместимы с технологией RDMA. Например, приведу выдержку из спеки про использование портов:
Data-Source Port:
A random port number generated by the source. This port number
MUST be unique within the source. Source Port together with
Global Source ID forms the TSI.
Data-Destination Port:
A globally well-known port number assigned to the given PGM
application.
Здесь дело в том, что технология RDMA использование портов не предполагает, хотя это реализовано в надстройке RDMA_CM. При этом, нам очень хотелось избежать применения RDMA_CM, так как это будет вносить дополнительные задержек при сопоставлении адресов с сетевыми устройствами.
RDMA_CM – это слой, который связывает привычную нам адресацию сети и интерфейс IBverbs. Вызовы RDMA_CM API как правило начинаются с префикса rdma, ibverbs начинаются с ibv. При этом IBverbs ничего не знает о RDMA_CM. Получается, что IBverbs — это базовая часть RDMA, а RDMA_CM — это более высокий уровень абстракции.
В нашей реализации Data-Destination Port игнорируется, а Data-Source Port по-прежнему является частью TSI. Надо оговориться, что это просто номер, который не указывает на реальный порт или сокет.
В общем, пришлось отступать от спеки там, где иначе переписать транспорт на RDMA было невозможно.
IP адресов, кстати, в RDMA тоже нет, но это меньшая проблема, так как есть прямой аналог — GID.

Посмотрим на результаты
На данный момент запустить OpenPGM по RDMA сетям нам удалось. Получившиеся результаты — одного порядка с показателями транспортного слоя, который реализован в торгово-клиринговых системах сейчас. Получившееся решение удовлетворяет нас и по функциональности, и по оценкам производительности.
Для тестирования мы написали несколько бенчмарков, которые эмулируют трафик работы торговой системы.
Локальный бенчмарк
Локальный бенчмарк без сетевого оверхэда обрабатывает 8 сообщений по 100 байт за 10 микросекунд для RDMA транспорта, против почти 20 микросекунд для UDP мультикаста.
В бенчмарке замеряется время каждой итерации (Time) и отдельно время затраченного процессорного времени (CPU). Разница между Time и CPU для Posix составляет почти четверть всего времени, а для RDMA эти два значения почти совпадают.
Это наглядно демонстрирует то, насколько RDMA интерфейс меньше времени проводит в ожидании и более эффективен.
Скрытый текст
Running rapid/mq.bench.perf.t
Run on (56 X 3500 MHz CPU s)
CPU Caches:
L1 Data 48 KiB (x56)
L1 Instruction 32 KiB (x56)
L2 Unified 1280 KiB (x56)
L3 Unified 43008 KiB (x2)
Load Average: 5.36, 6.53, 7.13
————————————————————————————
Benchmark Time CPU Iterations UserCounters…
————————————————————————————
MQBench/posix/pgm/100/8 19891 ns 14952 ns 46245 bytes_per_second=51.0272M/s items_per_second=535.059k/s
MQBench/posix/pgm/100/64 148981 ns 110035 ns 6330 bytes_per_second=55.469M/s items_per_second=581.635k/s
MQBench/posix/pgm/500/8 21334 ns 16285 ns 42363 bytes_per_second=234.248M/s items_per_second=491.253k/s
MQBench/posix/pgm/500/64 158560 ns 119189 ns 5841 bytes_per_second=256.043M/s items_per_second=536.962k/s
MQBench/rdma/pgm/100/8 10022 ns 9998 ns 68569 bytes_per_second=76.3116M/s items_per_second=800.185k/s
MQBench/rdma/pgm/100/64 57812 ns 57657 ns 11689 bytes_per_second=105.858M/s items_per_second=1.11M/s
MQBench/rdma/pgm/500/8 11044 ns 11018 ns 61158 bytes_per_second=346.237M/s items_per_second=726.111k/s
MQBench/rdma/pgm/500/64 65155 ns 64991 ns 7730 bytes_per_second=469.568M/s items_per_second=984.756k/s
Пинг-понг
Еще один бенчмарк — это пинг-понг по RDMA-сети. Нам удалось достичь пропускной способности транспорта 500 000 сообщений в секунду с RTT менее 5 микросекунд. То есть на сетевой hop приходится около 2.5 микросекунд времени отклика.
2.5 микросекунды — это все еще существенно больше, чем показывают штатные утилиты RDMA из rpm пакета perftest. Оверхэд тестируемого решения на основе OpenPGM получается около 1 мкс на hop, а значит, нам есть еще что оптимизировать.
Скрытый текст
mq_ping_lenovo1_pgmib 10000 events 9911 RTT 5016ns
mq_ping_lenovo1_pgmib 50000 events 48064 RTT 4992ns
mq_ping_lenovo1_pgmib 100000 events 92173 RTT 5004ns
mq_ping_lenovo1_pgmib 200000 events 170993 RTT 4941ns
mq_ping_lenovo1_pgmib 400000 events 306109 RTT 4910ns
mq_ping_lenovo1_pgmib 500000 events 360313 RTT 4849ns
mq_ping_lenovo1_pgmib 600000 events 403607 RTT 5008ns
mq_ping_lenovo1_pgmib 700000 events 435860 RTT 4939ns
mq_ping_lenovo1_pgmib 800000 events 495852 RTT 4858ns
Вместо заключения
Любопытно, что все началось с неудачного тендера. Навряд ли OpenPGM проектировался для сервисов с ультракоротким временем отклика, но при разумной оптимизации нам удалось добиться неплохих результатов. Таким образом, основываясь на полученных значениях, мы приняли решение использовать OpenPGM в качестве транспорта в нашей будущей технологической платформе для разработки высокопроизводительных торговых систем и протоколов для финансового рынка.
OpenPGM — это проект под лицензией GPL, а значит при его использовании и распространении нам придется выложить все внесенные изменения в OSS. Так что вполне возможно, что однажды у Биржи появится собственная открытая реализация ultra-low-latency messaging поверх RDMA. И, возможно, именно провалившийся тендер оказался лучшим вариантом развития событий.
ссылка на оригинал статьи https://habr.com/ru/articles/1044130/