Используемый
NodeJS, Socket.io
Проблема
Представьте, что есть 2 пользователя U1 и U2 , подключенных к приложению через Socket.io. Алгоритм следующий:
- U1 полностью теряет подключение к Интернету (например, отключает Интернет)
- U2 отправляет сообщение U1 .
- U1 еще не получает сообщение, потому что Интернет не работает
- Сервер обнаруживает отключение U1 по таймауту пульса
- U1 повторно подключается к socket.io
- U1 никогда не получает сообщение от U2 - я думаю, оно потеряно на шаге 4.
Возможное объяснение
Думаю, я понимаю, почему это происходит:
- на шаге 4 сервер убивает экземпляр сокета и очередь сообщений для U1 , а
- Более того, на шаге 5 U1 и сервер создают новое соединение (оно не используется повторно), поэтому, даже если сообщение все еще стоит в очереди, предыдущее соединение все равно теряется.
Нужна помощь
Как я могу предотвратить такую потерю данных? Я должен использовать ритм, потому что я не заставляю людей зависать в приложении навсегда. Кроме того, я должен предоставить возможность повторного подключения, потому что, когда я развертываю новую версию приложения, я хочу, чтобы время простоя было нулевым.
PS То, что я называю «сообщением», - это не просто текстовое сообщение, которое я могу сохранить в базе данных, но ценное системное сообщение, доставка которого должна быть гарантирована, иначе пользовательский интерфейс испортится.
Благодаря!
Дополнение 1
У меня уже есть система учетных записей пользователей. Более того, мое приложение уже сложное. Добавление статусов офлайн / онлайн не поможет, потому что у меня уже есть такие вещи. Проблема в другом.
Проверьте шаг 2. На этом шаге мы технически не можем сказать, перейдет ли U1 в автономный режим. , он просто теряет соединение, скажем, на 2 секунды, вероятно, из-за плохого интернета. Итак, U2 отправляет ему сообщение, но U1 не получает его, потому что для него все еще не работает Интернет (шаг 3). Шаг 4 необходим для обнаружения офлайн-пользователей, допустим, таймаут составляет 60 секунд. В конце концов, еще через 10 секунд интернет-соединение для U1 установлено, и он снова подключается к socket.io. Но сообщение от U2 потеряно в пространстве, потому что сервер U1 был отключен по таймауту.
Вот в чем проблема, мне не нужна 100% доставка.
Решение
- Соберите эмитент (имя и данные эмитента) у пользователя {}, идентифицированного случайным emitID. Отправить эмитент
- Подтвердите эмитент на стороне клиента (отправьте эмитент обратно на сервер с emitID)
- Если подтверждено - удалить объект из {}, идентифицированный emitID
- Если пользователь подключился повторно - проверьте {} для этого пользователя и прокрутите его, выполнив шаг 1 для каждого объекта в {}
- При отключении и / или подключении промывка {} для пользователя при необходимости
// Server
const pendingEmits = {};
socket.on('reconnection', () => resendAllPendingLimits);
socket.on('confirm', (emitID) => { delete(pendingEmits[emitID]); });
// Client
socket.on('something', () => {
socket.emit('confirm', emitID);
});
Решение 2 (вроде)
Добавлено 1 фев 2020.
Хотя на самом деле это не решение для Websockets, кому-то оно может пригодиться. Мы перешли с Websockets на SSE + Ajax. SSE позволяет подключаться от клиента, чтобы поддерживать постоянное TCP-соединение и получать сообщения от сервера в реальном времени. Чтобы отправлять сообщения от клиента на сервер - просто используйте Ajax. Есть недостатки, такие как задержка и накладные расходы, но SSE гарантирует надежность, потому что это TCP-соединение.
Поскольку мы используем Express, мы используем эту библиотеку для SSE https://github.com/dpskvn/express-sse , но вы можете выбрать ту, которая вам подходит.
SSE не поддерживается в IE и большинстве версий Edge, поэтому вам понадобится полифил: https://github.com/Yaffle/EventSource .