Jak zaimplementować kolejkę komunikatów przez Redis?

29

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 PUBSUBnie 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 BRPOPLPUSHto inteligentny projekt.

Czy możemy użyć BRPOPLPUSH?

Podstawowy projekt BRPOPLPUSHpolega 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 LREMjest 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, PUBSUBponieważ 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ę, aby PUBSUBnie 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.

djechlin
źródło

Odpowiedzi:

5

Jeśli chcesz użyć Redis do kolejki komunikatów w Node.js i nie masz nic przeciwko użyciu modułu do tego, możesz wypróbować RSMQ - Prostą kolejkę komunikatów Redis dla węzła. Nie było dostępne w czasie, gdy zadawano to pytanie, ale dziś jest realną opcją.

Jeśli chcesz samodzielnie zaimplementować kolejkę, jak podano w pytaniu, możesz przeczytać źródło RSMQ, ponieważ jest to tylko 20 ekranów kodu, które robią dokładnie to, o co prosisz.

Widzieć:

rsp
źródło
Zaakceptuję to, chyba że później dowiem się, że to naprawdę jest wadliwe, zepsute czy coś.
djechlin
22

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:

  • Słuchanie jest idempotentne.
  • Konsument zawsze nasłuchuje, a logika dławienia jest przetwarzana poza kodem logiki nasłuchu. RabbitMQ podsumowuje to, pozwalając konsumentowi ograniczyć liczbę niepakowanych wiadomości, które może mieć.
    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:

  • konsumenci powinni również sprawdzić, czy działają prace nad ponownym połączeniem.
  • konsumenci i tak mogą chcieć przeprowadzić ankietę dotyczącą nowej pracy, w celu uzyskania zwolnienia. Jeśli sonda rzeczywiście się powiedzie, powinno zostać wyemitowane ostrzeżenie, ponieważ powinno to nastąpić tylko między zużyciem na PUBSUB a sondą na RPOPLPUSH. Dlatego wiele sukcesów w ankiecie wskazuje na zepsuty system subskrypcji.

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ć.

djechlin
źródło
Nie jestem pewien, czy śledzę problem z blokowaniem konsumentów. Wydaje mi się, że jeśli nie ma pracy do przetworzenia, konsumenci powinni blokować, dopóki nie będzie, ale przypuszczam, że jeśli konsument robi również inne rzeczy, które mogą być inną historią, ale czy nie jest to większy problem w aplikacji a nie tyle w kolejce? IE nie blokowałoby wątków w większej aplikacji, co byłoby bardziej eleganckim rozwiązaniem, w którym wątek byłby w stanie powiadomić aplikację o pobraniu zadania z kolejki. Być może to właśnie użycie węzła powoduje komplikację.
AaronM
9
Jestem ciekawy, jak daleko zaszedłeś od sierpnia ubiegłego roku. Czy byłeś w stanie rozwiązać swoje problemy w sposób zadowalający? Jak je rozwiązałeś?
AaronM
3
AAA: tak jak @AaronM Chciałbym usłyszeć o twoich postępach.
bjornl,
Zgoda. Jak do tego doszło? Podoba mi się pomysł usunięcia RabbitMQ ze stosu i użycia Redis, który i tak tam jest. Mój problem polega na tym, jak zarejestrować konsumenta za pomocą RSMQ (lib node).
ra9r
@raiglstorfer nie pracował tam od dwóch lat: P nie krępuj się
poszukać i wysłać
0

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.

ra9r
źródło