{"id":14285,"url":"\/distributions\/14285\/click?bit=1&hash=346f3dd5dee2d88930b559bfe049bf63f032c3f6597a81b363a99361cc92d37d","title":"\u0421\u0442\u0438\u043f\u0435\u043d\u0434\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0442\u0440\u0430\u0442\u0438\u0442\u044c \u043d\u0430 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435 \u0438\u043b\u0438 \u043f\u0443\u0442\u0435\u0448\u0435\u0441\u0442\u0432\u0438\u044f","buttonText":"","imageUuid":""}

События сервера для мобильных web-приложений

Некоторое время назад возникла потребность реализовать в своём мобильном web-приложении оповещение клиента (фронт) о событиях, происходящих на сервере (бэк). Я рассматривал технологию Server Sent Events, как более простую альтернативу Websockets ("HTTP & text" vs. "TCP & binary").

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

Анализируя ситуацию, обнаружил такие вещи:

  • Сервер не всегда может определить, что клиент отвалился. В некоторых ситуациях сервер продолжает отправлять сообщения и сообщения безвозвратно пропадают в Сети.
  • Без помощи со стороны web-приложения у сервера нет возможности определить, это один браузер с одного мобильного устройства открыл два отдельных SSE-соединения или это два разных браузера с разных мобильных устройств открывают соединения из-за одной точки доступа.

У меня получились такие выводы:

  • Мобильное приложение при установлении SSE-соединений с сервером должно генерировать некий UUID, чтобы с его помощью сервер мог отличить, когда web-приложение подключается повторно после разрыва соединения, а когда подключаются разные web-приложения с одинаковыми внешними IP-адресами.
  • SSE лучше не использовать для доставки клиенту бизнес-данных (например, данных о товаре, добавленном в каталог), но использовать для доставки оповещения о том, что нужно запросить бизнес-данные (что в каталоге появился новый товар и что клиент может запросить данные о всех новых товарах). В этом случае, если какой-то сигнал не дойдёт то клиента, то пропущенный товар попадёт к клиенту при обработке следующего сигнала. Т.е., бизнес-логика строится из расчёта, что по SSE сервер отправляет (push) сигналы клиенту, а клиент запрашивает (pull) данные, связанные с сигналами, отдельными запросами.
  • IMHO, желательно, чтобы клиент отдельным HTTP-запросом отправлял на сервер подтверждение каждому полученному от него сообщению. В таком случае сервер получает шанс закрыть соединение, если клиент ушёл в offline (если подтверждение о получении сообщения не пришло в течение определённого timeout’а).

Более подробно — здесь.

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

0
4 комментария
Ярослав Яшин
> Без помощи со стороны web-приложения у сервера нет возможности определить, это один браузер с одного мобильного устройства открыл два отдельных SSE-соединения или это два разных браузера с разных мобильных устройств открывают соединения из-за одной точки доступа.

Что-то мне здесь стало страшно за безопасность. Механизм COOKIE давно придуман (это то, что вы называете далее "некий UUID"), ну а здесь это всё же Application ID. и идеально ему не доверять, и каждое мобильное приложение перед началом работы получает свой ID от сервера (ID очень "сложный", по сути в нем и логин и пароль)

Синхронизация между двумя (хорошо, если их две) удаленными точками может реализоваться достаточно просто списком событий (просто данные); тем самым можем допустить работу приложения какое-то время в офлайне (например, закидывать продукты в корзину; а сервер потом в пакете обновления "скажет", что этого нет, здесь уже другая цена - приложение, соответственно реагирует). Идентификатор обязательно последовательный - так контролируем пропуски. У каждого есть очередь на отправку и статус подтверждения получения. Если важно время - становится сложнее, но иногда достаточно окончания "транзакции" - т.е. есть сообщение до которого все предыдущие события должны быть корректно обработаны и о них можно забыть.

Ответить
Развернуть ветку
Alex Gusev
Автор

Вы правы, можно использовать механизм cookies. Но нужно предварительно "посадить" куку на клиента. К тому же в качестве EventSource'а может выступать не тот сервер, с которого закачивались исходники фронта (и ставилась кука):
```
const source = new EventSource('http://domain.com/path/to/sse/:id');
```
в этом случае альтернатив GET-переменной я не вижу.

каждое мобильное приложение перед началом работы получает свой ID от сервера (ID очень "сложный", по сути в нем и логин и пароль)

Ничего не имею против того, чтобы использовать его в качестве ID для канала. Вопрос не в том, что использовать в качестве ID, а в том, нужен ли такой ID или нет. IMHO, нужен.

тем самым можем допустить работу приложения какое-то время в офлайне ... а сервер потом в пакете обновления "скажет" ...

Тут другое направление, от бэка к фронту. Это бэк какое-то время работает в "оффлайн", отправляя данные на фронт, которые фронт не получает, т.к. отвалился. И у сервера нет возможности самостоятельно узнать, это надолго или навсегда.

Ответить
Развернуть ветку
Ярослав Яшин
> тут другое направление, от бэка к фронту.

Это двунаправленный канал с одинаковой реализацией и наличием точек обязательной синхронизации. Сервер тоже собирает очередь для отправки клиенту (например, сообщения чата; обновления цен корзины). В случае долгого полного оффлайна (что для приложения, обычно, всё же странно; хотя у меня есть ситуации, когда офлайн реально может быть несколько суток - обслуживание туристов в круизе) необходим явный контроль - не было синхронизации * часов - "извините, синхронизируйте данные с сервером".

> И у сервера нет возможности самостоятельно узнать, это надолго или навсегда.

Нет, никогда не будет и, в общем-то, не надо.

Ответить
Развернуть ветку
Anna Petrova

👀🤯

Ответить
Развернуть ветку
1 комментарий
Раскрывать всегда