http utrzymać przy życiu w czasach nowożytnych

92

Według autora haproxy, który wie co nieco o http:

Keep-alive został wynaleziony, aby zmniejszyć zużycie procesora na serwerach, gdy procesory były 100 razy wolniejsze. Ale nie jest powiedziane, że trwałe połączenia zajmują dużo pamięci, a nie mogą być używane przez nikogo poza klientem, który je otworzył. Dziś w 2009 roku procesory są bardzo tanie, a pamięć jest nadal ograniczona do kilku gigabajtów ze względu na architekturę lub cenę. Jeśli witryna wymaga utrzymywania aktywności, pojawia się prawdziwy problem. Silnie załadowane witryny często wyłączają funkcję utrzymywania aktywności, aby obsługiwać maksymalną liczbę jednoczesnych klientów. Prawdziwym minusem braku utrzymywania przy życiu jest nieco zwiększone opóźnienie pobierania obiektów. Przeglądarki podwajają liczbę jednoczesnych połączeń w witrynach, które nie są aktywne, aby to zrekompensować.

(z http://haproxy.1wt.eu/ )

Czy jest to zgodne z doświadczeniami innych ludzi? tj. bez utrzymywania przy życiu - czy wynik jest teraz ledwo zauważalny? (prawdopodobnie warto zauważyć, że w przypadku gniazd sieciowych itp. - połączenie jest utrzymywane jako „otwarte” niezależnie od statusu utrzymywania aktywności - i tak dla bardzo responsywnych aplikacji). Czy efekt jest większy w przypadku osób oddalonych od serwera - czy też istnieje wiele artefaktów do załadowania z tego samego hosta podczas ładowania strony? (Myślę, że takie elementy jak CSS, obrazy i JS w coraz większym stopniu pochodzą z sieci CDN obsługujących pamięć podręczną).

Myśli?

(nie jestem pewien, czy jest to sprawa serverfault.com, ale nie przekreślę posta, dopóki ktoś nie powie mi, żebym go tam przenieśli).

Michael Neale
źródło
1
Warto zauważyć, że w innym miejscu dokumentacji haproxy, która utrzymuje przy życiu, jest mowa w innych, korzystniejszych słowach. Bardzo chciałbym usłyszeć o doświadczeniach ludzi, zwłaszcza w przypadku masowego goszczenia.
Michael Neale,
„Zdobądź lepiej zaprojektowany serwer WWW / aplikacji”? :-) Nowsze projekty (takie jak Jetty) z obsługą połączeń kontynuacyjnych (podobnie jak) zasadniczo zmniejszają problemy z pamięcią / wątkami. Ponadto „kilka GB” brzmi jak okres ważności serwera 2008/2009 ;-)
3
Dla mnie to brzmi jak gadka. Dodatkowy RTT związany z ustawieniem nowego gniazda jest twardym fizycznym limitem, który jest często wystarczająco długi, aby był wykrywalny dla człowieka i nie można go zmniejszyć w ramach znanych praw fizyki. I odwrotnie, pamięć RAM jest tania, tańsza i nie ma powodu, aby bezczynne gniazdo zużywało więcej niż kilka kB.
Will Dean
2
ale co ciekawe, to nie jest tylko teoria - to jest autor haproxy. Słyszę tylko teorie i przypuszczenia.
Michael Neale,

Odpowiedzi:

141

Hej, skoro jestem autorem tego cytatu, odpowiem :-)

W dużych witrynach występują dwa poważne problemy: jednoczesne połączenia i opóźnienia. Współbieżne połączenia są spowodowane przez powolnych klientów, których pobieranie trwa długo, oraz przez bezczynne stany połączenia. Te bezczynne stany połączenia są spowodowane ponownym użyciem połączenia w celu pobrania wielu obiektów, znanym jako utrzymywanie przy życiu, które jest dodatkowo zwiększane przez opóźnienie. Gdy klient jest bardzo blisko serwera, może intensywnie korzystać z połączenia i zapewnić, że prawie nigdy nie jest bezczynny. Jednak gdy sekwencja się kończy, nikomu nie zależy na szybkim zamknięciu kanału, a połączenie pozostaje otwarte i nieużywane przez długi czas. To jest powód, dla którego wiele osób sugeruje użycie bardzo niskiego limitu czasu utrzymywania aktywności. Na niektórych serwerach, takich jak Apache, najniższy limit czasu, jaki można ustawić, to jedna sekunda i często jest to o wiele za dużo, aby wytrzymać duże obciążenia: jeśli masz przed sobą 20000 klientów i pobierają oni średnio jeden obiekt na sekundę, to 20000 połączeń zostanie nawiązanych na stałe. 20000 jednoczesnych połączeń na serwerze ogólnego przeznaczenia, takim jak Apache, jest ogromne, będzie wymagało od 32 do 64 GB pamięci RAM w zależności od załadowanych modułów i prawdopodobnie nie można mieć nadziei na osiągnięcie znacznie wyższego poziomu nawet po dodaniu pamięci RAM. W praktyce w przypadku 20000 klientów możesz nawet zobaczyć od 40000 do 60000 równoczesnych połączeń na serwerze, ponieważ przeglądarki będą próbowały zestawić 2 do 3 połączeń, jeśli mają wiele obiektów do pobrania. i prawdopodobnie nie możesz mieć nadziei na osiągnięcie dużo wyższego poziomu nawet po dodaniu pamięci RAM. W praktyce w przypadku 20000 klientów możesz nawet zobaczyć od 40000 do 60000 równoczesnych połączeń na serwerze, ponieważ przeglądarki będą próbowały zestawić 2 do 3 połączeń, jeśli mają wiele obiektów do pobrania. i prawdopodobnie nie możesz mieć nadziei na osiągnięcie dużo wyższego poziomu nawet po dodaniu pamięci RAM. W praktyce w przypadku 20000 klientów możesz nawet zobaczyć od 40000 do 60000 jednoczesnych połączeń na serwerze, ponieważ przeglądarki będą próbowały zestawić 2 do 3 połączeń, jeśli mają wiele obiektów do pobrania.

Jeśli zamkniesz połączenie po każdym obiekcie, liczba jednoczesnych połączeń dramatycznie spadnie. Rzeczywiście, spadnie o współczynnik odpowiadający średniemu czasowi pobierania obiektu w zależności od czasu między obiektami. Jeśli potrzebujesz 50 ms, aby pobrać obiekt (miniaturowe zdjęcie, przycisk itp.), A pobierasz średnio 1 obiekt na sekundę, jak powyżej, będziesz mieć tylko 0,05 połączenia na klienta, czyli tylko 1000 jednoczesne połączenia dla 20000 klientów.

Teraz liczy się czas na nawiązanie nowych połączeń. Klienci daleko zdalni będą doświadczać nieprzyjemnych opóźnień. W przeszłości przeglądarki używały dużej liczby równoczesnych połączeń, gdy funkcja utrzymywania aktywności była wyłączona. Pamiętam cyfry 4 na MSIE i 8 na Netscape. To naprawdę podzieliłoby średnie opóźnienie na obiekt o tyle. Teraz, gdy funkcja utrzymywania aktywności jest wszędzie obecna, nie widzimy już tak wysokich liczb, ponieważ zwiększa to dodatkowo obciążenie zdalnych serwerów, a przeglądarki dbają o ochronę infrastruktury Internetu.

Oznacza to, że w dzisiejszych przeglądarkach trudniej jest sprawić, by usługi, które nie działają, są tak samo responsywne, jak te, które działają. Ponadto niektóre przeglądarki (np .: Opera) używają heurystyki, aby próbować używać potokowania. Pipelining to skuteczny sposób korzystania z funkcji utrzymywania aktywności, ponieważ prawie eliminuje opóźnienia, wysyłając wiele żądań bez czekania na odpowiedź. Wypróbowałem to na stronie ze 100 małymi zdjęciami, a pierwszy dostęp jest około dwa razy szybszy niż bez utrzymywania przy życiu, ale następny dostęp jest około 8 razy szybszy, ponieważ odpowiedzi są tak małe, że liczy się tylko opóźnienie (tylko Odpowiedzi „304”).

Powiedziałbym, że najlepiej byłoby, gdybyśmy mieli trochę przestrajalnych przeglądarek, aby utrzymać połączenia między pobranymi obiektami i natychmiast porzucić je po zakończeniu strony. Ale niestety tego nie widzimy.

Z tego powodu niektóre witryny, które muszą instalować serwery ogólnego przeznaczenia, takie jak Apache z przodu, i które muszą obsługiwać dużą liczbę klientów, generalnie muszą wyłączyć funkcję utrzymywania aktywności. Aby zmusić przeglądarki do zwiększenia liczby połączeń, używają wielu nazw domen, aby pobieranie mogło być równoległe. Jest to szczególnie problematyczne w witrynach intensywnie korzystających z SSL, ponieważ konfiguracja połączenia jest jeszcze wyższa, ponieważ istnieje jedna dodatkowa podróż w obie strony.

Obecnie częściej obserwuje się, że takie strony wolą instalować lekkie nakładki, takie jak haproxy czy nginx, które nie mają problemu z obsługą dziesiątek do setek tysięcy jednoczesnych połączeń, umożliwiają utrzymywanie aktywności po stronie klienta i wyłączają je po stronie klienta. Strona Apache. Z tej strony koszt ustanowienia połączenia jest prawie zerowy pod względem mocy obliczeniowej procesora i niezauważalny pod względem czasu. W ten sposób zapewnia to, co najlepsze z obu światów: małe opóźnienie dzięki utrzymywaniu przy życiu z bardzo niskimi limitami czasu po stronie klienta i małą liczbą połączeń po stronie serwera. Wszyscy są szczęśliwi :-)

Niektóre produkty komercyjne dodatkowo poprawiają to, wykorzystując ponownie połączenia między systemem równoważenia obciążenia z przodu a serwerem i multipleksując wszystkie połączenia klientów za ich pośrednictwem. Gdy serwery są blisko LB, zysk nie jest dużo wyższy niż w poprzednim rozwiązaniu, ale często będzie to wymagało dostosowań w aplikacji, aby zapewnić, że nie ma ryzyka przechodzenia sesji między użytkownikami z powodu nieoczekiwanego współdzielenia połączenia między wieloma użytkownikami . W teorii to nigdy nie powinno mieć miejsca. Rzeczywistość jest zupełnie inna :-)

Willy Tarreau
źródło
1
Dziękuję za pełną i wyczerpującą odpowiedź! Byłem trochę zdezorientowany różnymi komentarzami na stronie o utrzymywaniu przy życiu - ale to wszystko ma sens.
Michael Neale
Co ciekawe - zauważyłem, że Chrome na Linuksie ponownie wykorzystuje utrzymywane połączenie przez kilka sekund - tj. Czas potrzebny do otwarcia kolejnej karty - ta druga karta była pod inną nazwą hosta, ale została rozwiązana za pomocą symbolu wieloznacznego DNS na ten sam serwer (masa wirtualny hosting) - iw ten sposób ponownie wykorzystał to samo połączenie! (spowodowało to pewne zaskoczenie, nie tego dobrego - oczywiście, jeśli utrzymanie przy życiu jest tylko po stronie klienta, jest w porządku).
Michael Neale
Słyszałem tylko, że „używaj czegokolwiek innego niż apache, a to nic wielkiego”. To, co ekstrapolowałem, to „wyłącz mod_php i pasażera, a wtedy nawet Apache może mieć szansę na walkę”.
coolaj86
@ CoolAJ86: chodzi o to, żeby absolutnie nie walić w Apache i osobiście go używam. Chodzi o to, że im bardziej ogólny serwer, tym mniej opcji trzeba skalować. Niektóre moduły wymagają modelu pre-fork, wtedy nie można skalować do ogromnej liczby połączeń. Ale jak wyjaśniono, nie jest to wielka sprawa, ponieważ można ją połączyć z innym wolnym komponentem, takim jak haproxy. Dlaczego ktoś miałby wymieniać wszystko w tym przypadku? Lepiej zainstaluj haproxy, niż przechodząc przez kłopoty z ponownym wdrożeniem aplikacji na innym serwerze!
Willy Tarreau
22

Od czasu, gdy to zostało napisane (i opublikowane tutaj na stackoverflow), mamy teraz serwery takie jak nginx, które zyskują na popularności.

Na przykład nginx może utrzymywać otwarte 10000 połączeń utrzymujących aktywność w jednym procesie przy użyciu tylko 2,5 MB (megabajtów) pamięci RAM. W rzeczywistości łatwo jest utrzymać wiele tysięcy połączeń przy bardzo małej ilości pamięci RAM, a jedynymi ograniczeniami, które napotkasz, będą inne ograniczenia, takie jak liczba otwartych uchwytów plików lub połączeń TCP.

Utrzymywanie przy życiu było problemem nie z powodu jakiegokolwiek problemu z samą specyfikacją utrzymywania przy życiu, ale z powodu modelu skalowania opartego na procesach Apache i zhakowania serwera, którego architektura nie została zaprojektowana do jej dostosowania.

Szczególnie problematyczny jest Apache Prefork + mod_php + keep-alives. Jest to model, w którym każde połączenie będzie nadal zajmować całą pamięć RAM zajmowaną przez proces PHP, nawet jeśli jest całkowicie bezczynny i pozostaje otwarty tylko jako utrzymywanie przy życiu. To nie jest skalowalne. Ale serwery nie muszą być projektowane w ten sposób - nie ma szczególnego powodu, dla którego serwer musi utrzymywać każde utrzymywane połączenie w osobnym procesie (zwłaszcza nie, gdy każdy taki proces ma pełny interpreter PHP). PHP-FPM i model przetwarzania serwera oparty na zdarzeniach, taki jak ten w nginx, elegancko rozwiązują problem.

Aktualizacja 2015:

SPDY i HTTP / 2 zastępują funkcję podtrzymywania aktywności HTTP czymś jeszcze lepszym: możliwością nie tylko utrzymywania połączenia i wykonywania wielu żądań i odpowiedzi za jego pośrednictwem, ale także ich multipleksowania, dzięki czemu odpowiedzi mogą być wysyłane w dowolnej kolejności i równolegle, a nie tylko w kolejności, w jakiej zostały poproszone. Zapobiega to blokowaniu szybszych odpowiedzi przez wolne odpowiedzi i eliminuje pokusę utrzymywania przez przeglądarki wielu otwartych połączeń równoległych z jednym serwerem. Technologie te dodatkowo podkreślają wady podejścia mod_php i zalety czegoś w rodzaju serwera sieciowego opartego na zdarzeniach (lub przynajmniej wielowątkowego) połączonego oddzielnie z czymś takim jak PHP-FPM.

thomasrutter
źródło
2

rozumiałem, że ma to niewiele wspólnego z procesorem, ale z opóźnieniem w otwieraniu powtarzających się gniazd na drugą stronę świata. nawet jeśli masz nieskończoną przepustowość, opóźnienie połączenia spowolni cały proces. wzmocnione, jeśli Twoja strona zawiera dziesiątki obiektów. nawet trwałe połączenie ma opóźnienie żądania / odpowiedzi, ale jest ono mniejsze, gdy masz średnio 2 gniazda, ponieważ jedno powinno przesyłać strumieniowo dane, a drugie może blokować. Ponadto router nigdy nie założy, że gniazdo jest połączone, zanim nie pozwoli ci na pisanie do niego. Wymaga pełnego uścisku dłoni w obie strony. znowu nie twierdzę, że jestem ekspertem, ale zawsze tak to postrzegałem. naprawdę fajny byłby w pełni protokół ASYNC (nie, nie w pełni chory protokół).

catchpolenet
źródło
tak - takie byłoby moje założenie. może to kompromis - jest moment, w którym opóźnienie (spowodowane odległością) oznacza, że ​​jest to prawdziwy problem
Michael Neale
ok, więc nowoczesna typografia wymagałaby połączenia się z serwerem proxy, który jest w pobliżu (być może). ale czy rozszerzysz to pytanie na to, czy serwery proxy powinny używać trwałych połączeń?
catchpolenet
@Michael Neale również, z powodu takich rzeczy, jak powolny start TCP, faktyczna kara za opóźnienie jest znacznie gorsza niż można by się spodziewać.
MartinodF
być może kompromisem jest znacznie krótszy czas oczekiwania. jeśli masz kopie zapasowe żądań, po co zamknąć gniazdo i zacząć od nowa? nawet 1 sekunda umożliwiłaby załadowanie strony z pełną trwałością, a następnie natychmiastowe zamknięcie gniazd.
catchpolenet
2

Bardzo długie utrzymywanie przy życiu może być przydatne, jeśli używasz CDN typu „origin pull”, takiego jak CloudFront lub CloudFlare. W rzeczywistości może się to okazać szybsze niż brak CDN, nawet jeśli obsługujesz całkowicie dynamiczną zawartość.

Jeśli długo utrzymujesz się przy życiu, tak że każdy punkt PoP ma w zasadzie stałe połączenie z twoim serwerem, to gdy użytkownicy po raz pierwszy odwiedzają twoją witrynę, mogą wykonać szybkie uzgadnianie TCP z lokalnym PoP zamiast powolnego uzgadniania z tobą. (Samo światło potrzebuje około 100 ms, aby przejść przez światłowód w połowie drogi dookoła świata, a ustanowienie połączenia TCP wymaga przesłania trzech pakietów tam iz powrotem. SSL wymaga trzech podróży w obie strony ).

mjs
źródło
1
Kusiło mnie, żeby dać +1, ale twój drugi akapit zawiera tę niepoprawną uwagę, że światło potrzebuje tylko 10 ms na podróż przez połowę świata. 10 ms prędkości światła w próżni to 3000 km, a 10 ms we włóknie to niewiele więcej niż 2000 km; pół drogi dookoła świata (wzdłuż powierzchni) to 20 000 km. To byłoby 100 ms - gdyby tylko światłowód prowadził bezpośrednio z Londynu do Sydney, zamiast prawdopodobnie opływać Afrykę drogą morską lub
piramidy
@pyramids Masz rację, albo to wpisałem, albo po prostu oszukałem. Zaktualizuję.
mjs