Використовується
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 .