Istnieje kilka problemów, w zależności od celów.
Po pierwsze, promuje tylko dostępność zasobów na zapleczu. Zastanów się, czy masz 5 serwerów obsługujących żądania kolejek na zapleczu. Jeśli jeden z tych serwerów ulegnie awarii, oczekujące żądanie powinno wrócić do kolejki i zostać ponownie dostarczone do jednego z pozostałych 4 serwerów.
Podczas gdy te serwery zaplecza są przetwarzane, serwery frontonu trzymają się faktycznych, inicjujących żądań. Jeśli jeden z tych serwerów frontowych zakończy się niepowodzeniem, połączenia te zostaną całkowicie utracone, a pierwotny klient będzie musiał ponownie przesłać żądanie.
Założeniem może być to, że prostsze systemy front end są mniej narażone na awarie, a to z pewnością odnosi się do awarii oprogramowania. Ale karty sieciowe, zasilacze, dyski twarde itp. Są dość agnostyczne wobec takich fałszywych nadziei dla człowieka i karą wszystkich jednakowo. Rozważ to, mówiąc o ogólnej dostępności.
W zakresie projektowania, back-end jest prostym procesem czekającym na kolejkę komunikatów JMS i przetwarzającym każdy komunikat w momencie jego pojawienia się. Istnieje wiele przykładów tego, a każdy serwer JMS będzie odpowiadał na wysokim poziomie. Wszystko, czego potrzebujesz, to zapewnienie, że obsługa komunikatów jest transakcyjna, tak więc jeśli przetwarzanie komunikatu nie powiedzie się, wiadomość pozostaje w kolejce i może zostać ponownie dostarczona do innej procedury obsługi wiadomości.
Głównym wymogiem kolejki JMS jest możliwość utworzenia klastra. Sam serwer JMS jest pojedynczym punktem awarii w systemie. Zgubiłem serwer JMS, a twój system jest prawie martwy w wodzie, więc musisz mieć możliwość połączenia serwera z serwerem i umożliwienia konsumentom i producentom odpowiedniego przełączenia awaryjnego. Ponownie, jest to specyficzny serwer JMS, większość robi to, ale jest dość rutyna w świecie JMS.
Na samym początku sprawy stają się nieco trudniejsze, ponieważ serwery frontowe są mostem od synchronicznego świata żądania REST do asynchronicznego świata procesorów back-end. Żądanie REST jest zgodne z typowym schematem RPC zużywającym ładunek żądania z gniazda, utrzymującym połączenie otwartym, przetwarzającym wyniki i dostarczającym wyniki z powrotem do początkowego gniazda.
Aby zamanifestować to rozdanie, należy spojrzeć na asynchroniczny serwlet obsługujący wprowadzony serwlet 3.0 i jest dostępny w Tomcat 7, najnowszym Jetty (nie jestem pewien, która wersja), Glassfish 3.x i inne.
W tym przypadku, co należy zrobić, gdy przychodzi żądanie, konwertuj nominalnie synchroniczne wywołanie serwletu do wywołania asynchronicznego za pomocą HttpServletRequest.startAsync(HttpServletRequest request, HttpServletResponse response)
.
Zwraca AsynchronousContext, a po uruchomieniu pozwala serwerowi zwolnić wątek przetwarzania. Następnie robisz kilka rzeczy.
- Wyodrębnij parametry z żądania.
- Utwórz unikalny identyfikator dla żądania.
- Utwórz nowy ładunek zlecenia z powrotem ze swoich parametrów.
- Powiąż identyfikator z AsyncContext i zachowaj kontekst (np. Umieszczając go na mapie aplikacji).
- Prześlij żądanie z powrotem do kolejki JMS.
W tym momencie początkowe przetwarzanie jest wykonywane, a użytkownik po prostu wraca z programu doGet (lub usługi lub cokolwiek innego). Ponieważ nie wywołałeś AsyncContext.complete(), serwer nie zamknie połączenia z serwerem. Ponieważ masz magazyn AsyncContext na mapie według identyfikatora, jest on na razie przydatny do bezpiecznego przechowywania.
Teraz, po przesłaniu wniosku do kolejki JMS, zawiera on: identyfikator żądania (który został wygenerowany), wszelkie parametry dotyczące żądania oraz identyfikację faktycznego serwera, który wysłał żądanie. Ten ostatni fragment jest ważny, ponieważ wyniki przetwarzania muszą powrócić do swojego źródła. Początek jest identyfikowany przez identyfikator żądania i identyfikator serwera.
Po uruchomieniu serwera frontowego uruchomiono także wątek, którego zadaniem jest odsłuchanie kolejki odpowiedzi JMS. Po skonfigurowaniu połączenia JMS może ustawić filtr taki jak "Daj mi tylko wiadomości dla ID serwera ABC123". Można też utworzyć unikalną kolejkę dla każdego serwera frontonu, a serwer zaplecza użyje identyfikatora serwera do określenia kolejki, na którą ma zwrócić odpowiedź.
Gdy procesory zaplecza pobierają komunikat, pobierają identyfikator żądania i parametry, wykonują pracę, a następnie pobierają wynik i umieszczają je w kolejce odpowiedzi JMS. Gdy wynik zostanie zwrócony, doda oryginalny identyfikator serwera i oryginalny identyfikator żądania jako właściwości wiadomości.
Tak więc, jeśli pierwotnie otrzymano żądanie dla serwera Front End Server ABC123, procesor zaplecza będzie zwracał wyniki do tego serwera. Następnie wątek tego odbiorcy zostanie powiadomiony, gdy pojawi się komunikat. Zadanie wątków odbiornika polega na pobraniu tego komunikatu i umieszczeniu go w wewnętrznej kolejce na serwerze frontonu.
Ta wewnętrzna kolejka jest wspierana przez pulę wątków, której zadaniem jest przesłać żądanie z powrotem do oryginalnego połączenia. Czyni to poprzez wyodrębnienie oryginalnego ID żądania z komunikatu, przeglądając Asynchroniczny kontekst z omawianej wcześniej mapy wewnętrznej, a następnie wysyłając wyniki do HttpServletResponse powiązanego z Asynchronicznym Kontekstem. Na koniec wywołuje AsyncContext.complete() (lub podobną metodę), aby poinformować serwer, że skończyłeś i zezwolić na zwolnienie połączenia.
Do utrzymania porządku powinieneś mieć inny wątek na serwerze frontonu, którego zadaniem jest wykrywanie, kiedy żądania oczekują na mapie zbyt długo. Częścią oryginalnej wiadomości powinien być czas rozpoczęcia żądania. Wątek ten może się obudzić co sekundę, przeskanować mapę pod kątem żądań, a za każdą, która była tam zbyt długo (np. 30 sekund), może skierować żądanie do kolejnej wewnętrznej kolejki, zużywanej przez kolekcję programów obsługi mających na celu informowanie klient, którego czas upłynął.
Chcesz te wewnętrzne kolejki, aby główna logika przetwarzania nie utknęła, czekając na klienta, aby zużyć dane. Może to być powolne połączenie lub coś takiego, więc nie chcesz blokować wszystkich innych oczekujących żądań, aby poradzić sobie z nimi jeden po drugim.
Na koniec musisz wziąć pod uwagę, że możesz otrzymać komunikat z kolejki odpowiedzi dla żądania, które nie istnieje już na twojej wewnętrznej mapie. Po pierwsze, żądanie mogło przekroczyć limit czasu, więc nie powinno już tam być. Po drugie, ten serwer frontonu mógł zostać zatrzymany i zrestartowany, więc wewnętrzna mapa oczekującego żądania będzie po prostu pusta. W tym momencie, jeśli wykryjesz, że masz odpowiedź na żądanie, które już nie istnieje, powinieneś po prostu go odrzucić (dobrze, zaloguj się, a następnie odrzuć).
Nie można ponownie użyć tych żądań, nie ma czegoś takiego, jak system równoważenia obciążenia wracający do klienta. Jeśli klient zezwala na wykonywanie wywołań zwrotnych za pośrednictwem opublikowanych punktów końcowych, to z pewnością można po prostu zamówić inną procedurę obsługi wiadomości JMS. Ale to nie jest coś typu REST, REST na tym poziomie dyskusji jest bardziej klient/serwer/RPC.
Co do obsługi ramek Asynchroniczne serwlety na wyższym poziomie niż surowe serwlety (takie jak Jersey dla JAX-RS lub coś podobnego), nie mogę powiedzieć. Nie wiem, jakie ramy wspierają go na tym poziomie. Wydaje się, że jest to cecha Jersey 2.0, która nie jest jeszcze dostępna. Mogą być i inni, musicie się rozejrzeć. Nie naprawiaj także w Servlet 3.0. Servlet 3.0 to po prostu standaryzacja technik stosowanych w poszczególnych pojemnikach przez pewien czas (w szczególności Jetty), więc warto przyjrzeć się opcjom specyficznym dla kontenera poza serwletem 3.0.
Ale koncepcje są takie same. Duży wybór na wynos to odbiornik kolejki odpowiedzi z odfiltrowanym połączeniem JMS, wewnętrzna mapa żądań do AsyncContext oraz wewnętrzne kolejki i pule wątków do rzeczywistej pracy w aplikacji.
Czy można użyć systemu równoważenia obciążenia? – Henry
Co znalazłeś do tej pory? – miku
@Henry może być tym, czego chce, to utrzymywanie kolejki zgłoszeń połączonych. Nawet jeśli wszystkie serwery są zajęte, żadne żądania nie powinny zostać odrzucone, powinny być w puli, czekając na swoją kolej. – Subin