Message flow в real time приложениях

Обсуждаем, как правильно строить приложения
Ответить
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Message flow в real time приложениях

Сообщение sda »

Есть сокет-сервер и клиенты, которые к нему подключаются. Когда на сервере случается экшен (какое-либо действие), нужно уведомить об этом клиентов. Это чем-то похоже на реалтаймовые многопользовательские игры и похоже, что в них вот такой message flow. То есть пользователь запрашивает у сервера экшен login, а сервер затем всем клиентам кому это может быть интересно отправляет событие onLogin. Или пользователь отправляет экшен move, который перемещает игрока в другую точку, а сервер всем клиентам рассылает событие onMove, чтобы они обновили свой UI в соответствии с состоянием на сервере.

Хорошо. Теперь, допустим есть типичное чат-приложение и нужно отображать всех пользователей, кто в данный момент находится в чате. Как я это вижу:

1. В момент загрузки приложения клиент отправляет запрос на получение пользователей, которые сейчас онлайн и рендерит список пользователей
2. Клиент подписывается на события onJoin (вошел в чат) и onLeave (вышел из чата)
3. Когда на клиент приходит событие onJoin/onLeave, клиент корректирует свой список добавляя/удаляя из него пользователей.

Но если после 1 шага, но до того как выполнится шаг 2 кто-то покинет чат, то у клиента будут неверные данные. Как с этим бороться? Как правильно построить архитектуру приложения, чтобы избежать подобных ошибок?
Аватара пользователя
rugabarbo
Сообщения: 1063
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

Re: Message flow в real time приложениях

Сообщение rugabarbo »

В любом многопользовательском приложении с негарантированной доставкой данных правило простое: данные всегда должны быть консистентны на сервере. То есть в игре, если вы стреляете в противника, обсчитывать выстрел должен сервер. Нужно забыть на клиенте про всяческие гарантии и разрабатывать с расчётом на то, что часть передачи данных между сервером и клиентом постоянно теряется. Поэтому событийной синхронизации в таких архитектурах всегда недостаточно. Обычно вводят дополнительную синхронизацию состояний, которая срабатывает через равные периоды времени. Например, клиент каждые 5 секунд говорит серверу: "Дай-ка мне весь список пользователей!" - и обновляет его целиком. Полагаться только на события сервера в многопользовательских сетевых приложениях нельзя (по многим причинам, не только из-за указанного вами кейса).
Аватара пользователя
rugabarbo
Сообщения: 1063
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

Re: Message flow в real time приложениях

Сообщение rugabarbo »

То есть в вашем случае я бы после шага 2 (сразу же после подписания на событие onJoin) запрашивал повторно с клиента полный список пользователей и выставлял таймер в 5 секунд. Через пять секунд опять запрашивал полный список онлайн-пользователей и выставлял таймер. И так вплоть до выхода человека из чата.

С сообщениями ситуация сложнее. Нужно на клиенте проверять порядок сообщений (проверять на клиенте, что ID каждого следующего сообщения равен [previousID + 1]). Соответственно, приватные сообщения должны иметь отдельную нумерацию. В этом случае с клиента нужно будет периодически отсылать на сервер запрос: "Дай-ка мне номер последнего сообщения и последнего приватного сообщения" - после чего их проверять на наличие в клиенте. Если последних сообщений на клиенте нет, значит, они были потеряны, и их надо запросить повторно.

Ну и так далее. У вас должны быть периодические "контрольные синхронизации" различных данных на клиенте (по его же инициативе):
* полного списка онлайн
* ID последнего полученного сообщения
* ID последнего приватного сообщения
* и т.д.
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Message flow в real time приложениях

Сообщение sda »

А можете привести пример каким образом часть данных может теряться, если tcp гарантирует доставку?

Просто, вы мне по-сути предлагаете делать polling сервера, в то время как между клиентом и сервером открыто постоянное соединение и сервер может пушить обновления на клиент по tcp протоколу с гарантией доставки пакетов.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Message flow в real time приложениях

Сообщение zelenin »

1-2 пункты должны происходить в рамках одной транзакции - регистрация клиента
Аватара пользователя
rugabarbo
Сообщения: 1063
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

Re: Message flow в real time приложениях

Сообщение rugabarbo »

sda писал(а):А можете привести пример каким образом часть данных может теряться, если tcp гарантирует доставку?

Просто, вы мне по-сути предлагаете делать polling сервера, в то время как между клиентом и сервером открыто постоянное соединение и сервер может пушить обновления на клиент по tcp протоколу с гарантией доставки пакетов.
Ну, так скажем, я смотрю немного вперёд. Вы же спросили про архитектуру, а не про обработку одной конкретной ситуации, вот я вам и рассказываю заведомо про архитектуру. По вашему первому посту очевидно, что вы идеализируете клиент-серверное общение в многопользовательской асинхронной среде. У вас пока идеальная картина мира: "Пришёл клиент, я его подключил, подписал на события и теперь шлю ему только маленькие события с сервера по их возникновении" - на практике эта картина недостижима (хоть в TCP, хоть в UDP-виде).

TCP вам гарантирует, что отправитель будет в курсе, дошёл ли пакет до получателя. Ну и порядок отправки пакетов. Это всё. Казалось бы с точки зрения гарантий тут всё ОК, но основная же проблема многопользовательской сетевой среды в том, что она полностью асинхронна и спроектировать весь набор событий и реакций на них очень сложно. То есть проблема именно в недостижимости идеальной синхронизации клиента на основе одних лишь событий сервера. Я уже не говорю про сложность тестирования всевозможных кейсов. Например, что делать, если во время отправки данных клиенту интернет отвалился? Если сервер счёл по своему таймауту, что клиент умер, предпринял нужное число реконнектов и зафиксировал клиент как отвалившийся, а клиент опять стучится через пару секунд (говорит, мол, "Хехей, я обработал твоё последнее событие!")? Конечно, вы скажете, что есть специальные события дисконнекта как на клиенте, так и на сервере - но вы их во всём разнообразии сочетаний всё равно не сможете идеально обрабатывать. И тут вам на помощь приходит периодический polling.

Вот Зеленин предлагает делать 1-2 в одной транзакции, но если между двумя этими событиями клиент тупанёт (сеть отвалится, например, человек в метро едет), то сервер повиснет с этой своей транзакцией на пару секунд в ожидании второго события от клиента. Это не есть хорошо.

То есть можно придумывать кучу схем, в которых вы можете слать только с сервера на клиент, но они будут все сложны и негарантированы архитектурно (даже элементарно за счёт багов и человеческого фактора). Может быть в простейшем чате и прокатит подписать клиента на пару событий, добавить одну транзакцию и обойтись без поллинга, но как только приложение начнёт расти - вы всё равно придёте к периодческому запросу состояний с клиента на сервер.
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Message flow в real time приложениях

Сообщение sda »

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

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

Но еще помоему может быть проблема на стороне сервера, который запущен в несколько процессов или тредов, из-за конкурентных запросов, в тот момент когда один запрос уже выбрал список пользователей из базы и еще не успел отдать их на клиент, как в этот момент другой запрос меняет состояние одного из пользователей. Но вроде считается хорошей практикой из многопоточного входа делать однопоточный, чтобы избежать этих проблем.
Аватара пользователя
rugabarbo
Сообщения: 1063
Зарегистрирован: 2015.06.21, 16:21
Контактная информация:

Re: Message flow в real time приложениях

Сообщение rugabarbo »

Ну вот такими "патчами" (diff-ами всякими) ваша архитектура будет постоянно обрастать, пока не придёте-таки к синхронизации состояний через поллинг (:
Ответить