Dlaczego Redis do kolejkowania?
Mam wrażenie, że Redis może być dobrym kandydatem do wdrożenia systemu kolejkowania. Do tego momentu korzystaliśmy z naszej bazy danych MySQL z odpytywaniem lub RabbitMQ. Z RabbitMQ mieliśmy wiele problemów - biblioteki klienckie są bardzo ubogie i mają błędy, i nie chcielibyśmy poświęcać zbyt wiele godzin programistycznych na ich naprawę, kilka problemów z konsolą zarządzania serwerami itp. I na razie będąc co najmniej, nie bierzemy pod uwagę milisekund ani poważnego zwiększania wydajności, więc dopóki system ma architekturę, która inteligentnie obsługuje kolejkę, prawdopodobnie jesteśmy w dobrej formie.
Okej, więc to jest tło. Zasadniczo mam bardzo klasyczny, prosty model kolejek - kilku producentów produkujących pracę i kilku konsumentów zużywających pracę, a zarówno producenci, jak i konsumenci muszą być w stanie inteligentnie skalować. Okazuje się, że naiwny PUBSUB
nie działa, ponieważ nie chcę, aby wszyscy abonenci korzystali z pracy, chcę tylko, aby jeden subskrybent otrzymał pracę. Przy pierwszym przejściu wydaje mi się, że BRPOPLPUSH
to inteligentny projekt.
Czy możemy użyć BRPOPLPUSH?
Podstawowy projekt BRPOPLPUSH
polega na tym, że masz jedną kolejkę roboczą i kolejkę postępu. Kiedy konsument otrzymuje pracę, atomowo wpycha przedmiot do kolejki postępu, a gdy kończy pracę, to LREM
jest to. Zapobiega to zakłócaniu pracy w przypadku śmierci klientów i sprawia, że monitorowanie jest dość łatwe - na przykład możemy stwierdzić, czy występuje problem, który powoduje, że konsumenci zajmują dużo czasu, oprócz informowania, czy jest duża liczba zadań.
Zapewnia
- praca jest dostarczana dokładnie jednemu konsumentowi
- praca kończy się w kolejce postępu, więc nie może zrobić dziury, jeśli konsument
Wady
- Wydaje mi się dość dziwne, że najlepszy projekt, który znalazłem, w rzeczywistości nie używa,
PUBSUB
ponieważ wydaje się, że na tym skupia się większość postów na blogu o kolejkach nad Redis. Mam wrażenie, że brakuje mi czegoś oczywistego. Jedynym sposobem, w jaki widzę, abyPUBSUB
nie używać dwukrotnie zadań, jest po prostu wysłanie powiadomienia o nadejściu pracy, które konsumenci mogą wtedy nie blokowaćRPOPLPUSH
. - Nie można żądać więcej niż jednego elementu pracy na raz, co wydaje się być problemem z wydajnością. Nie jest to duży jak na naszą sytuację, ale raczej oczywiste jest, że ta operacja nie została zaprojektowana z myślą o dużej przepustowości ani takiej sytuacji
- W skrócie: czy brakuje mi czegoś głupiego?
Dodałem także tag node.js, ponieważ to jest język, z którym najczęściej mam do czynienia. Węzeł może oferować pewne uproszczenia we wdrażaniu, biorąc pod uwagę jego jednowątkowy i nieblokujący charakter, ale ponadto korzystam z biblioteki redis węzła i rozwiązania powinny lub mogą być wrażliwe na jego mocne i słabe strony.
źródło
Do tej pory napotkałem pewne trudności, które chciałbym tutaj udokumentować.
Jak radzisz sobie z logiką ponownego połączenia?
Jest to trudny problem i szczególnie trudny problem przy projektowaniu i wdrażaniu kolejki komunikatów. Wiadomości muszą być w stanie ustawić się w kolejce gdzieś, gdy klienci są offline, więc prosty sub-pub nie jest wystarczająco silny, a konsumenci muszą ponownie połączyć się w stanie nasłuchiwania. Blokowanie wyskakujących okienek jest trudnym stanem do utrzymania, ponieważ są one stanem niedbałym odsłuchu . Słuchanie powinno być operacją idempotentną, ale mając do czynienia z rozłączeniem w odniesieniu do blokującego popu, masz przyjemność bardzo intensywnie zastanawiać się, czy rozłączenie nastąpiło tuż po tym, jak operacja się powiodła, czy tuż przed niepowodzeniem. To nie jest nie do pokonania, ale jest niepożądane.
Ponadto operacja odsłuchu powinna być możliwie najprostsza. Idealnie powinien mieć te właściwości:
W szczególności poszedłem ze słabym projektem, w którym powrót do popu blokującego zależał od powodzenia poprzednich operacji, które były kruche i wymagały intensywnego myślenia.
Teraz faworyzuję rozwiązanie Redis PUBSUB + RPOPLPUSH. To oddziela powiadomienie o pracy od zużycia pracy, co pozwala nam wybrać czyste rozwiązanie odsłuchowe. PUBSUB jest odpowiedzialny tylko za powiadomienie o pracy. Atomowa natura RPOPLPUSH odpowiada za konsumpcję i przekazywanie pracy dokładnie jednemu konsumentowi. Na początku to rozwiązanie wydawało się niepotrzebnie skomplikowane w porównaniu do blokującego popu, ale teraz widzę, że komplikacja wcale nie była niepotrzebna; to rozwiązało trudny problem.
Jednak to rozwiązanie nie jest dość trywialne:
Należy pamiętać, że konstrukcja PUBSUB / RPOPLPUSH ma również problemy ze skalowaniem. Każdy konsument otrzymuje lekkie powiadomienie o każdej wiadomości, co oznacza, że ma to niepotrzebne wąskie gardło. Podejrzewam, że można używać kanałów do dzielenia pracy, ale prawdopodobnie jest to trudny projekt, aby dobrze się spisać.
źródło
Zatem największym powodem wyboru RabbitMQ zamiast Redis są scenariusze awarii i klastrowanie.
Ten artykuł naprawdę najlepiej to wyjaśnia, więc podam tylko link:
https://aphyr.com/posts/283-jepsen-redis
Wartownik Redis i ostatnio klastrowanie redis nie są w stanie obsłużyć wielu bardzo podstawowych scenariuszy awarii, co czyni z niego zły wybór dla kolejki.
RabbitMQ ma swój własny zestaw problemów, jednak biorąc pod uwagę, że jest niewiarygodnie solidny w produkcji i stanowi dobrą kolejkę komunikatów.
Oto post dla królika:
https://aphyr.com/posts/315-jepsen-rabbitmq
Kiedy spojrzysz na teorię CAP (spójność, dostępność i obsługa partycji), możesz wybrać tylko 2 z 3. Wykorzystujemy RMQ dla CP (spójność i obsługa partycji) z naszym ładowaniem wiadomości, jeśli nie jesteśmy dostępni, to nie jest koniec świata. Aby nie utracić wiadomości, używamy ignorowania do obsługi partycji, aby nie utracić wiadomości. Duplikaty mogą być obsługiwane, ponieważ źródło zarządza UUID.
źródło