Załóżmy, że istnieje serwer HTTP, który akceptuje połączenia, a następnie w jakiś sposób czeka na pełne przesłanie nagłówków. Zastanawiam się, jaki jest najczęstszy sposób jego wdrożenia, a jakie są pozostałe zalety i wady. Mogę tylko myśleć o tych:
Wielu pracowników blokujących jest dobrych, ponieważ:
- Jest bardziej responsywny.
- Łatwiejsze wprowadzanie nowych połączeń (pracownicy odbierają je osobiście, a nie z zewnątrz czekają, aż doda je do listy zsynchronizowanej).
- Wykorzystanie procesora równoważy się automatycznie (bez dodatkowego wysiłku) wraz ze wzrostem i spadkiem liczby połączeń.
- Mniejsze zużycie procesora (zablokowane wątki są usuwane z pętli wykonawczej i nie wymagają żadnej logiki do przeskakiwania między klientami).
Pojedynczy nieblokujący pracownik jest dobry, ponieważ:
- Zużywa mniej pamięci.
- Mniej podatne na leniwych klientów (którzy łączą się z serwerem i wysyłają nagłówki powoli lub wcale nie wysyłają).
Jak zapewne widać, moim zdaniem wiele wątków roboczych wydaje się ogólnie nieco lepszym rozwiązaniem. Jedynym problemem jest to, że łatwiej jest zaatakować taki serwer.
Edycja (więcej badań): Niektóre zasoby, które znalazłem w Internecie ( Tysiące wątków i blokowanie I / O - stary sposób pisania serwerów Java jest znowu nowy (i znacznie lepszy) autorstwa Paula Tymy) sugeruje, że blokowanie jest ogólnie lepsze, ale Nadal nie wiem, jak radzić sobie z fałszywymi połączeniami.
PS Nie sugeruj używania biblioteki lub aplikacji do tego zadania. Bardziej interesuje mnie wiedza o tym, jak to naprawdę działa lub może działać, niż mieć to.
PSS Podzieliłem logikę na wiele części, a ta obsługuje tylko akceptowanie nagłówków HTTP. Nie przetwarza ich.
źródło
Odpowiedzi:
Nie ma srebrnej kuli
W praktyce zależy to ...
tl; dr - łatwe rozwiązanie, użyj nginx ...
Bloking:
Na przykład Apache domyślnie stosuje schemat blokowania, w którym proces jest rozwidlany dla każdego połączenia. Oznacza to, że każde połączenie potrzebuje własnej przestrzeni pamięci, a sama liczba narzutów związanych z przełączaniem kontekstu wzrasta wraz ze wzrostem liczby połączeń. Ale zaletą jest to, że po zamknięciu połączenia kontekst może zostać zlikwidowany, a dowolną / całą pamięć można łatwo odzyskać.
Podejście wielowątkowe byłoby podobne, ponieważ narzut związany z przełączaniem kontekstu rośnie wraz z liczbą połączeń, ale może być bardziej wydajny pod względem pamięci w kontekście współużytkowanym. Problem z takim podejściem polega na tym, że zarządzanie pamięcią współużytkowaną w bezpieczny sposób jest trudne. Podejścia do przezwyciężania problemów z synchronizacją pamięci często obejmują własny narzut, na przykład blokowanie może zamrozić główny wątek przy obciążeniach intensywnie obciążających procesor, a użycie niezmiennych typów dodaje dużo niepotrzebnego kopiowania danych.
AFAIK, stosowanie podejścia wieloprocesowego na blokującym serwerze HTTP jest ogólnie preferowane, ponieważ zarządzanie / odzyskiwanie pamięci jest bezpieczniejsze / prostsze w bezpieczny sposób. Odśmiecanie staje się problemem, gdy odzyskiwanie pamięci jest tak proste, jak zatrzymanie procesu. W przypadku długotrwałych procesów (np. Demona) ta cecha jest szczególnie ważna.
Podczas gdy narzut związany z przełączaniem kontekstu może wydawać się nieistotny w przypadku niewielkiej liczby pracowników, wady stają się bardziej istotne, gdy obciążenie skaluje się do setek do tysięcy równoczesnych połączeń. W najlepszym wypadku przełączanie kontekstu skaluje O (n) do liczby obecnych pracowników, ale w praktyce najprawdopodobniej jest gorzej.
Tam, gdzie serwery wykorzystujące blokowanie mogą nie być idealnym wyborem dla dużych obciążeń We / Wy, są one idealne do pracy wymagającej dużej mocy procesora, a przekazywanie wiadomości jest utrzymywane na minimalnym poziomie.
Nieblokujący:
Nieblokowanie byłoby czymś w rodzaju Node.js lub nginx. Są one szczególnie znane ze skalowania do znacznie większej liczby połączeń na węzeł pod obciążeniem intensywnym we / wy. Zasadniczo, gdy ludzie osiągną górną granicę, jaką serwery oparte na wątkach / procesach mogą obsłużyć, zaczęli szukać alternatywnych opcji. Jest to również znane jako problem C10K (tj. Zdolność do obsługi 10 000 jednoczesnych połączeń).
Nieblokujące serwery asynchroniczne mają zwykle wiele cech charakterystycznych dla podejścia wielowątkowego z blokowaniem, polegające na tym, że należy uważać, aby uniknąć obciążeń intensywnie wykorzystujących procesor, ponieważ nie chcemy przeciążać głównego wątku. Zaletą jest to, że narzut związany z przełączaniem kontekstu jest zasadniczo wyeliminowany, a przekazanie komunikatu kontekstowego staje się nieproblemowe.
Chociaż może nie działać w przypadku wielu protokołów sieciowych, bezstanowa natura HTTPs działa szczególnie dobrze w przypadku architektur nieblokujących. Dzięki kombinacji odwrotnego proxy i wielu nieblokujących serwerów HTTP możliwe jest identyfikowanie i trasowanie wokół węzłów doświadczających dużego obciążenia.
Nawet na serwerze, który ma tylko jeden węzeł, bardzo często konfiguracja obejmuje jeden serwer na rdzeń procesora, aby zmaksymalizować przepustowość.
Obie:
„Idealny” przypadek użycia byłby kombinacją obu. Odwrotny serwer proxy z przodu poświęcony żądaniom routingu u góry, a następnie połączenie serwerów blokujących i nieblokujących. Nie blokuje zadań IO, takich jak serwowanie treści statycznych, zawartości pamięci podręcznej, treści HTML. Blokowanie zadań obciążających procesor, takich jak kodowanie obrazów / wideo, przesyłanie strumieniowe treści, dzielenie numerów, zapisywanie w bazie danych itp.
W Twoim przypadku:
Jeśli po prostu sprawdzasz nagłówki, ale nie przetwarzasz żądań, to zasadniczo to, co zasadniczo opisujesz, to zwrotny serwer proxy. W takim przypadku zdecydowanie wybrałbym podejście asynchroniczne.
Sugerowałbym przejrzenie dokumentacji wbudowanego odwrotnego proxy nginx .
Na bok:
Przeczytałem ten zapis z linka, który podałeś i ma sens, że async był kiepskim wyborem dla ich konkretnej implementacji. Problem można podsumować w jednym stwierdzeniu.
Budowali państwową platformę. W takim przypadku podejście asynchroniczne oznaczałoby, że będziesz musiał stale zapisywać / ładować stan za każdym razem, gdy zmienia się kontekst (tj. Po uruchomieniu zdarzenia). Ponadto po stronie SMTP wykonują dużo pracy wymagającej dużej mocy obliczeniowej.
Wygląda na to, że mieli dość słabą znajomość asynchronizacji, w wyniku czego przyjęli wiele złych założeń.
źródło