В своей статье сооснователь Ably Мэтью О’Риордан анализирует некоторые общие проблемы, которые они видят для компаний в сфере ставок и делиться некоторыми предложениями архитектурных шаблонов проектирования, которые могут быть полезными для других разработчиков.

Проблема #1. Теоретически неограниченное и часто непредсказуемое количество игроков, получающих обновления во время матча

Чаще всего поток событий приходит на серверы разработчиков приложений от поставщиков спортивных данных (Опта или Спортрадар), применяется некоторая бизнес-логика, и затем данные должны быть отправлены на каждое устройство игроков, которые хотят сделать ставку.

Например, когда забит гол, вы можете захотеть, чтобы каждый пользователь вашего сервиса в любой точке земного шара получил это обновление в течение 200 мс. Обычно проблема заключается в том, что вы хотите, чтобы целевое время ожидания было удовлетворено, не важно сколько у вас пользователей, один или миллионы, причем без изменения архитектуры по мере увеличения количества игроков.

Шаблон №1 — Издатель-подписчик

Этот шаблон едва ли можно назвать новым, он известен с 1987 г, однако это до сих пор хороший подход к асинхронному распределению данных в реальном времени. Этот паттерн предполагает 2 роли, Издатель для отправки сообщений, Подписчик для получения этих сообщений. Шаблон определяет, что Издатель (обычно ваш сервер) публикует сообщения без какого-либо знания о количестве подписчиков. А Подписчики регистрируются, чтобы получать сообщения без каких-либо знаний об Издателях. Это гарантирует, что 2 роли специально разделены посредником в качестве промежуточного ПО, получающего сообщения и распространяющего их до Подписчиков.

Как это помогает?

Это обеспечивает масштабирование ваших приложений теоретически безграничному количеству подписчиков без каких-либо изменений в дизайне вашей системы, тем самым сохраняя ваш стек простым, в то же время давая вам необходимое масштабирование. Брокер промежуточного ПО отвечает за масштабирование. Если вы выберите в качестве решения брокера сообщений, имеющего доказанную масштабируемость, то, приняв паттерн Издатель-Подписчик, по мере того, как вы масштабируете только один компонент, необходимо решить проблему предсказуемым способом.

Проблема 2. Синхронизация данных

Первая проблема описывает, как данные, как сообщения, распространяются на устройства, но не учитывает, как вы сохраняете данные об игре в синхронизированном состоянии на всех устройствах.

Например, ваше приложение может поддерживать ставки на рынки в лайве. Поскольку каждое событие происходит во время игры, ваше приложение должно отражать эти изменения в реальном времени как в пользовательском интерфейсе, так и в локальном хранилище. Проблема заключается в целостности данных и пропускной способности. Если вы публикуете все рынки для каждой линии, это может быть весьма неэффективным и привести к значительной нагрузке на пропускную способность на устройствах ваших пользователей. Возможно, что более важно, это может ухудшить пользовательский опыт у людей с медленным соединением или с дорогостоящей пропускной способностью. Однако, если вы отправляете только обновления данных, как гарантировать, что поддерживается целостность данных, т.е. вам нужно, чтобы все обновления доставлялись надежно и в порядке?

Шаблон 2.1 — Последовательные патчи JSON

JSON Patch — это стандарт, который определяет, как вы можете отправлять только дельту для объекта JSON когда он изменяется. Например, если у вас есть таблица всех игроков с их статистикой, и только у одного изменилась статистика после того как забили гол, то патч может выглядеть примерно так:

Как это помогает?

JSON патч предоставляет средства для эффективной отправки дельт для объекта JSON, и таким образом сокращает накладные расходы на пропускную способность. Однако, патч JSON не предоставляет абсолютного решения:

  • Вам нужно получить объект JSON на начальном этапе
  • Патчи JSON должны быть применены точно в том порядке, в котором они были сгенерированы — пропавший или недействительный патч приведет к полной потере целостности данных.

Шаблон для решения этой проблемы:

  • Настроить сообщения, которые нужно сохранить
  • Опубликовать оригинальный объект JSON на канале, и затем все патчи JSON
  • Клиенты при подключении получают историю каналов и подписываются на будущие патчи JSON. История предоставляет средства для создания объекта JSON из исходного объекта и всех патчей, а подключенный канал обеспечивает постоянное получение происходящий обновлений в целях обеспечения целостности.
  • Если клиент потеряет непрерывность канала (это может случиться, если клиент отключился более чем на 2 минуты), приложение просто повторяет предыдущий шаг.

Шаблон 2.2 — CRDT (Conflict-free Replicated Data Types)

источник pvsm.ru

CRDT — это бесконфликтные реплицированные типы данных. В отличие от патча JSON, этот шаблон позволяет нескольким сторонам одновременно обновлять базовый объект данных. Каждое обновление затем распространяется на все остальные стороны, и алгоритм гарантирует, что после того, как все обновления будут применены всеми участвующими сторонами, базовый объект данных станет в конечном итоге согласованным, независимо от порядка обновления.

CRDT обеспечивает очень сложный способ обеспечения согласованности данных, даже если несколько сторон меняют данные одновременно. Однако, для обеспечения возможных гарантий последовательности существуют ограниченные типы данных с конкретными ограничениями.

CRDT чаще используется в совместных приложениях, похожих на Google Docs, где несколько пользователей могут одновременно обновлять контент. Riak является одним из немногих решений для баз данных, предоставляющих встроенную поддержку CRDT.

Проблема №3. Восходящие игровые события

Задачи 1 и 2 рассматривают проблемы масштабирования данных вниз по потоку на устройства пользователей. Однако, участвующие в ставках на игры могут иметь восходящий компонент, т.е. пользователи на устройствах могут делать ставки, участвовать в чате, или сотрудничать с членами команды.

Обычно это обрабатывается простым HTTP-запросом на ваши сервера, которые, в свою очередь, запускают некоторую бизнес-логику, которая может синхронно реагировать (как часть ответа на HTTP-запрос) или асинхронно (позже возвращаться на устройство в виде сообщения).

Проблемы разработчиков приложений:

  • Использование HTTP-запроса синхронным образом увеличивает вероятность отказа операционной системы при изменении состояния сети. Пока клиент ожидает ответа, состояние сети может измениться, и основное TCP-соединение для HTTP запроса будет закрыто. Если запрос потерпел неудачу из-за TCP-соединения, его возможно придется повторить, но если операция не является идемпотентной, это может привести к неожиданному поведению для пользователей (например, он может поставить две одинаковые ставки).
  • Если внезапный всплеск активности, возможно, из-за непредвиденного изменения в матче, такого как удаление игрока, тогда вашим серверам необходимо будет немедленно принять эту нагрузку. Вам нужно заранее предсказать нагрузку от каждой игры и подстраховаться, что у вас есть достаточная мощность сервиса для всплесков.
  • HTTP не предоставляет никаких гарантий последовательности, т.е. более поздний запрос может прилететь до более раннего запроса, когда они находятся близко друг к другу.

Шаблон №3 — Очереди сообщений, серверная архитектура и потоки

Традиционные очереди сообщений спроектированы для обеспечения ориентированного на сообщения средства связи между компонентами системы, а именно устройствами и серверами в этом случае. Подобно промежуточному программному обеспечению в шаблоне Издатель-подписчик, очередь сообщений действует как брокер, распространяющий сообщения для каждого пользователя. Если ваше промежуточное программное обеспечение для обмена сообщениями является масштабируемым и отказоустойчивым, то разъединение каждого компонента в системе гарантирует, что отказ или перегрузка в одной области системы не повлияет ни на какую другую часть системы.

Обратите внимание, что в отличие от шаблона Издатель-подписчик, где каждое сообщение доставляется всем подписчикам, очереди сообщений обычно доставляют сообщения только один раз одному из потребителей (технически AMQP может предоставить гарантию как минимум одной, однако почти во всех ситуациях происходит только одна доставка). Сообщения обычно работают по FIFO (первый пришел — первый ушел), гарантируя, что несмотря на какое-либо отставание, которое происходит, если потребители не могут сразу обрабатывать сообщения, то всегда будут работать в первую очередь с самыми старыми сообщениями.

Как это помогает?

Путем отделения издателей данных от потребителей данных и внедрения брокера промежуточного программного обеспечения очереди сообщений вы создаете отказоустойчивую систему. Например:

  • Огромные всплески активности могут замедлить ответную реакцию клиентов, т.к. воркерам приходится работать с накопившимися сообщениями, но клиенты не будут испытывать сбоев.
  • Если порядок событий от устройства к серверу важен, очередь сообщений может обеспечить этот порядок.
  • Сообщения с устройств передаются непосредственно в очередь, и устройство получает подтверждение получения, немедленно уведомляющее устройство о том, что сообщение получено.
Шаблоны проектирования для разработчиков приложений в сфере ставок на спорт
Оценка