Używany
NodeJS, Socket.io
Problem
Wyobraź sobie, że jest dwóch użytkowników U1 i U2 połączonych z aplikacją przez Socket.io. Algorytm jest następujący:
- U1 całkowicie traci połączenie z Internetem (np. Wyłącza Internet)
- U2 wysyła wiadomość do U1 .
- U1 nie otrzymuje jeszcze wiadomości, ponieważ Internet nie działa
- Serwer wykrywa rozłączenie U1 na podstawie limitu czasu pulsu
- U1 ponownie łączy się z socket.io
- U1 nigdy nie otrzymuje wiadomości od U2 - chyba zgubiła się w kroku 4.
Możliwe wyjaśnienie
Myślę, że rozumiem, dlaczego tak się dzieje:
- Krok 4 na serwer zabija instancji gnieździe i kolejkę wiadomości do U1 oraz
- Ponadto w kroku 5 U1 i Serwer tworzą nowe połączenie (nie jest ono ponownie wykorzystywane), więc nawet jeśli wiadomość nadal znajduje się w kolejce, poprzednie połączenie i tak zostaje utracone.
Potrzebuję pomocy
Jak mogę zapobiec tego rodzaju utracie danych? Muszę używać beatów, bo ludzie nie wiszą na zawsze w aplikacji. Muszę też nadal dawać możliwość ponownego połączenia, ponieważ wdrażając nową wersję aplikacji, nie chcę żadnych przestojów.
PS To, co nazywam „wiadomością”, to nie tylko wiadomość tekstowa, którą mogę przechowywać w bazie danych, ale wartościowa wiadomość systemowa, której dostarczenie musi być zagwarantowane, w przeciwnym razie interfejs użytkownika spieprzy.
Dzięki!
Dodatek 1
Mam już system kont użytkowników. Co więcej, moja aplikacja jest już złożona. Dodawanie statusów offline / online nie pomoże, ponieważ mam już tego typu rzeczy. Problem jest inny.
Sprawdź krok 2. Na tym etapie technicznie nie możemy powiedzieć, czy U1 przejdzie w tryb offline , po prostu traci połączenie, powiedzmy na 2 sekundy, prawdopodobnie z powodu złego internetu. Więc U2 wysyła mu wiadomość, ale U1 jej nie otrzymuje, ponieważ internet nadal nie działa (krok 3). Krok 4 jest potrzebny do wykrycia użytkowników offline, powiedzmy, limit czasu wynosi 60 sekund. W końcu w ciągu kolejnych 10 sekund połączenie internetowe dla U1 jest aktywne i ponownie łączy się z socket.io. Ale wiadomość z U2 została utracona w przestrzeni, ponieważ na serwerze U1 została rozłączona przez przekroczenie limitu czasu.
To jest problem, nie chciałbym dostarczać w 100%.
Rozwiązanie
- Zbierz emisję (nazwę emisji i dane) w użytkowniku {}, identyfikowanym przez losowy identyfikator emitID. Wyślij emisję
- Potwierdź emisję po stronie klienta (wyślij emisję z powrotem do serwera z emitID)
- Jeśli potwierdzone - usuń obiekt z {} identyfikowany przez emitID
- Jeśli użytkownik ponownie się połączył - sprawdź {} dla tego użytkownika i przejdź przez niego, wykonując krok 1 dla każdego obiektu w {}
- Po rozłączeniu lub / i podłączeniu w razie potrzeby spłukać {} dla użytkownika
// Server
const pendingEmits = {};
socket.on('reconnection', () => resendAllPendingLimits);
socket.on('confirm', (emitID) => { delete(pendingEmits[emitID]); });
// Client
socket.on('something', () => {
socket.emit('confirm', emitID);
});
Rozwiązanie 2 (trochę)
Dodano 1 lutego 2020 r.
Chociaż nie jest to tak naprawdę rozwiązanie dla Websockets, komuś może się przydać. Przeprowadziliśmy migrację z Websockets do SSE + Ajax. SSE umożliwia łączenie się z klientem w celu utrzymania trwałego połączenia TCP i odbierania wiadomości z serwera w czasie rzeczywistym. Aby wysłać wiadomości od klienta na serwer - po prostu użyj Ajax. Istnieją wady, takie jak opóźnienia i narzuty, ale SSE gwarantuje niezawodność, ponieważ jest to połączenie TCP.
Ponieważ używamy Express, używamy tej biblioteki do SSE https://github.com/dpskvn/express-sse , ale możesz wybrać tę, która Ci odpowiada.
SSE nie jest obsługiwane w IE i większości wersji Edge, więc potrzebujesz polyfill: https://github.com/Yaffle/EventSource .