Skupiając się na HTTP Keep-Alive Timeout Pokażę ci, jak ustawić czasy bezczynności, aby połączenia były ponownie wykorzystywane bez blokowania wątków. Wyjaśnię konkretne wartości, pokażę typowe pułapki i przedstawię wypróbowane i przetestowane konfiguracje dla nginx, Apache i system operacyjny.
Punkty centralne
- RównowagaZbyt krótki zwiększa liczbę uścisków dłoni, zbyt długi blokuje wątki.
- WartościPrzeważnie 5-15 s i 100-500 żądań na połączenie.
- KoordynacjaKoordynacja limitów czasu klienta, LB i zapory sieciowej.
- Przypadki szczególneWebSockets, SSE, Long Polling oddzielnie.
- MonitoringMonitorowanie otwartych gniazd, FD i opóźnień.
Krótkie wyjaśnienie HTTP Keep-Alive
Utrzymuję połączenia TCP z Keep-Alive aby kilka żądań korzystało z tej samej linii. Oszczędza to powtarzających się uzgodnień TCP i TLS i zmniejsza obciążenie sieci. CPU-zauważalnie. Jest to szczególnie korzystne w przypadku wielu małych plików, takich jak ikony, JSON lub CSS. Każde nowe połączenie, którego udało się uniknąć, zmniejsza liczbę przełączeń kontekstu i odciąża procedury jądra. W testach porównawczych z wysokim udziałem GET, ogólny czas trwania jest znacznie skrócony, ponieważ generowanych jest mniej pakietów SYN/ACK, a więcej czasu obliczeniowego przepływa do logiki aplikacji.
Szybko mierzę efekt: średnie ruchome opóźnienia stają się płynniejsze, a liczba nowych połączeń TCP na sekundę spada. Nie osiągam tego za pomocą magii, ale poprzez Ponowne użycie połączenia i rozsądne limity. Ważne jest, aby Keep-Alive nie zastępowało szybkiego renderowania lub buforowania. Skraca czas oczekiwania na granicy sieci, podczas gdy sama aplikacja musi nadal reagować wydajnie. Oba te czynniki razem zwiększają Wydajność zauważalnie.
Zrozumienie właściwego limitu czasu
Limit czasu określa, jak długo nieaktywne połączenie pozostaje otwarte, zanim serwer je zamknie. zamknięcia. Jeśli ustawię zbyt krótki czas, klienci będą stale otwierać nowe połączenia TCP, co Nad głową jest podniesiony. Jeśli ustawię go zbyt długo, bezczynne połączenia zaparkują cennych pracowników lub wątki. Sztuka polega na równowadze między ponownym użyciem a zużyciem zasobów. Testuję praktycznie: najpierw ustawiam go z grubsza, a następnie dostosowuję za pomocą testów obciążenia.
Zwracam również uwagę na związek między czasem odpowiedzi a bezczynnymi oknami. Jeśli typowa interakcja użytkownika między dwoma kliknięciami wynosi 2-4 sekundy, limit czasu 5-15 sekund zwykle obejmuje rzeczywisty wzorzec. Krótkie wywołania API mogą z łatwością tolerować 5-10 sekund, obciążenia medialne 10-15 sekund. Ważne jest, aby nie przesadzić: zbyt długie timeouty rzadko prowadzą do więcej Przepustowość, ale często prowadzą do zablokowania Zasoby. Mogę to szybko rozpoznać po rosnącej liczbie otwartych gniazd i wysokich wartościach FD.
Czyste rozdzielenie typów limitów czasu
Rzecznie rozróżniam między Limit czasu bezczynności (Keep-Alive), Limit czasu odczytu/nagłówka (jak długo serwer czeka na przychodzące żądania) i Limit czasu wysyłania/zapisu (jak długo wysyłanie w kierunku klienta jest tolerowane). Kategorie te spełniają różne zadania:
- Limit czasu bezczynności: Kontroluje ponowne użycie i czas parkowania nieaktywnych połączeń.
- Limit czasu odczytu/nagłówka: Chroni przed powolnymi klientami (slow lorises) i połowicznie wysłanymi nagłówkami.
- Limit czasu wysyłania/zapisu: Zapobiega niekończącemu się oczekiwaniu serwera na powolny odbiór u klienta.
Na stronie nginx Celowo używam header_timeout/read_timeout i send_timeout na kontekst (http/serwer/lokalizacja) oprócz keepalive_timeout. Od nowszych wersji opcjonalnie ustawiam keepalive_time, aby ograniczyć maksymalny czas życia połączenia, nawet jeśli pozostaje ono aktywne. W Apacz Używam również RequestReadTimeout (mod_reqtimeout) i sprawdzić Limit czasu (globalny) oddzielony od KeepAliveTimeout. Oddzielenie to jest ważnym elementem zapobiegającym wiązaniu zasobów bez żadnych realnych korzyści.
Zalecane wartości w praktyce
Dla wydajnych środowisk ustawiam limit czasu utrzymania połączenia na 5-15 sekund i 100-500 żądań na połączenie. Zakres ten zapewnia dobre wskaźniki ponownego wykorzystania połączeń i utrzymuje liczbę nieaktywnych połączeń na niskim poziomie. Na nginx Używam keepalive_timeout 10s jako wartości początkowej i keepalive_requests 200. Jeśli ruch jest duży, zwiększam go umiarkowanie, jeśli widzę zbyt wiele nowych połączeń TCP. Jeśli ruch jest niewielki, ponownie obniżam tę wartość, aby uniknąć zalewu bezczynnego ruchu.
Ci, którzy wchodzą głębiej, skorzystają z jasnego procesu dostrajania z punktami pomiarowymi. W tym celu podsumowuję moje wytyczne w praktycznym przewodniku, który opisuje ścieżkę od pomiaru przez konfigurację do kontroli. Aby szybko rozpocząć, odsyłam do moich kroków w Optymalizacja Keep-Alive. Jak kontrolować Ponowne użycie i uniknąć niespodzianek. Ostatecznie liczą się niskie opóźnienia i stabilność. Przepustowość.
Ryzyko związane z długimi limitami czasu
Długi limit czasu sprawia, że połączenia są sztuczne otwarty i blokuje pracowników, nawet jeśli nie następuje żadne żądanie. Powoduje to pęcznienie gniazd i zwiększa liczbę deskryptorów plików. Jeśli proces osiągnie swoje limity, widzę odrzucanie błędów akceptacji lub kolejki podczas nawiązywania połączenia. Pamięć rośnie, garbage collectory lub alokatory kosztują dodatkowy czas, a opóźnienie wzrasta. W przypadku błędu, klienci wysyłają następnie do gniazd, które są już zamknięte i otrzymują kryptowalutę Błąd.
Unikam tego, ustawiając umiarkowane wartości i regularnie sprawdzając metryki. Jeśli liczba bezczynnych połączeń zbytnio wzrasta przy niskim obciążeniu, obniżam limit czasu. Jeśli widzę wiele nowych połączeń na sekundę podczas szczytów ruchu, ostrożnie zwiększam go małymi krokami. W ten sposób utrzymuję Pojemność Użyteczność i zapobieganie martwym połączeniom. Rezultatem jest płynniejszy system z mniejszą liczbą Wskazówki na zakrętach.
Konfiguracja: nginx, Apache i warstwa systemu operacyjnego
Zaczynam na poziomie serwera WWW i ustawiam limit czasu i limity. Na nginx Ustawiam keepalive_timeout 5-15s i keepalive_requests 100-500. W Apache z event-MPM łączę KeepAlive On, KeepAliveTimeout 5-15 i MaxKeepAliveRequests 100-500. Następnie kalibruję worker lub pule wątków zgodnie z oczekiwanym obciążeniem. Zapobiega to produktywności bezczynnych keep-alives. Automaty do gry wiązanie.
Zwiększam limity i kolejki na poziomie systemu operacyjnego. Ustawiam ulimit -n na co najmniej 100 000, dostosowuję net.core.somaxconn i tcp_max_syn_backlog i sprawdzam obsługę TIME_WAIT. Zapewnia to, że jądro i proces mają wystarczająco dużo Zasoby zapewnić. Na koniec weryfikuję ścieżki z karty sieciowej poprzez równoważenie IRQ do aplikacji. Pozwala mi to na rozpoznanie wąskich gardeł w odpowiednim czasie i utrzymanie Opóźnienie niski.
| Komponent | Dyrektywa/Ustawienie | Zalecenie | Wskazówka |
|---|---|---|---|
| nginx | keepalive_timeout | 5–15 s | Krótszy przy małym ruchu, dłużej przy wielu małych żądaniach |
| nginx | keepalive_requests | 100–500 | Recykling związków i redukcja Wycieki |
| Apache (zdarzenie) | KeepAliveTimeout | 5–15 s | Event-MPM zarządza bezczynnością bardziej efektywnie niż prefork |
| System operacyjny | ulimit -n | ≥ 100.000 | Więcej otwartych FD dla wielu Gniazda |
| System operacyjny | net.core.somaxconn | Wzrost | Mniej odrzuconych połączeń pod Obciążenie szczytowe |
Odwrotne proxy i ponowne wykorzystanie upstream
Zawsze myślę, że keep-alive end-to-end. Za serwerem brzegowym często znajduje się łańcuch reverse proxy → serwery aplikacji. W przypadku nginx aktywuję własny Baseny Keep Alive (upstream keepalive, keepalive_requests, keepalive_timeout), ustawić proxy_http_version 1.1 i usunąć „Connection: close“. To również oszczędza mi wewnętrzny handshake i odciążanie backendów aplikacji (Node.js, Java, PHP-FPM). W Apache z mod_proxy utrzymuję również trwałe połączenia z serwerami zaplecza i ograniczam je na miejsce docelowe, aby hotspot nie zmonopolizował puli.
Mierzę osobno: wskaźnik ponownego użycia Klient→Edge i Edge→Backend. Jeśli widzę dobre ponowne użycie na krawędzi, ale wiele nowych połączeń z backendem, selektywnie zwiększam pule upstream. Pozwala mi to na skalowanie bez globalnego zwiększania limitów czasu frontendu.
Pracownicy, wątki i limity systemu operacyjnego
Nie wymiaruję pracowników, zdarzeń i wątków według pożądanych wartości, ale według profil obciążenia. Aby to zrobić, monitoruję aktywne żądania, bezczynnych pracowników, wykorzystanie pętli zdarzeń i przełączniki kontekstu. Jeśli wątki są zaparkowane w trybie bezczynności, obniżam limit czasu lub maksymalny czas bezczynności na wątek. Jeśli widzę 100 procent CPU przez cały czas, sprawdzam kolejki akceptacji, dystrybucję IRQ i stos sieciowy. Niewielkie korekty limitów FD i zaległości często robią dużą różnicę. Efekty.
Planuję headroom realistycznie. Rezerwa 20-30 procent w wątkach i FD zapewnia bezpieczeństwo w przypadku szczytów. Jeśli przesadzę, stracę pamięć podręczną i zwiększy się marnotrawstwo. Jeśli go nie wykorzystam, żądania wylądują w kolejkach lub wygasną. Właściwe przecięcie Pojemność i wydajność utrzymuje opóźnienia na niskim poziomie i chroni Stabilność.
Koordynacja limitów czasu klienta, load balancera i firewalla
Ustawiłem limity czasowe na całej ścieżce, aby nie było ślepych zaułków. Połączenia są tworzone. Klienci idealnie zamykają się minimalnie wcześniej niż serwer. Load balancer nie może odciąć krótszego czasu, w przeciwnym razie zobaczę nieoczekiwane resety. Uwzględniam wartości bezczynności NAT i firewalla, aby połączenia nie były tracone na ścieżce sieciowej. zniknąć. Strojenie to zapobiega retransmisjom i wygładza krzywe obciążenia.
Używam przejrzystych diagramów, aby łańcuch był zrozumiały: Klient → LB → serwer WWW → aplikacja. Dokumentuję czasy bezczynności, czasy odczytu/zapisu i strategie ponawiania dla każdego łącza. Jeśli zmieniam wartość, sprawdzam sąsiadów. Dzięki temu ścieżka jest spójna, a wyniki pomiarów powtarzalne. Taka dyscyplina oszczędza czas w Rozwiązywanie problemów i zwiększa niezawodność.
Bezpieczeństwo: Ochrona przed powolnymi lorisami i bezczynnymi nadużyciami
Otwarte limity czasu, które są zbyt hojne Powierzchnie ataku. Dlatego ustawiam limity, które pozwalają na legalne ponowne użycie, ale utrudniają złośliwe utrzymywanie ich otwartych. W nginx pomagają limity header i read_timeout, request_headers_size i twardy górny limit dla keepalive_requests. W Apache używam mod_reqtimeout i ograniczam równoległe połączenia na IP. Limity szybkości i limit_conn w nginx również chronią przed zalewem wielu bezczynnych gniazd. W przypadku długo działających punktów końcowych oddzielam dedykowane pule, aby ataki na strumienie nie wiązały zwykłych pracowników API.
Przypadki szczególne: Long Polling, SSE i WebSockets
Długie strumienie zderzają się z krótkimi Limity czasu i potrzebują własnych reguł. Technicznie oddzielam te punkty końcowe od klasycznego API i tras zasobów. Dla SSE i WebSockets ustawiam wyższe limity czasu, dedykowane pule pracowników i twarde limity na IP. Utrzymuję połączenie przy życiu za pomocą heartbeatów lub ping/pong i szybko rozpoznaję rozłączenia. W ten sposób strumienie nie blokują wątków dla regularnych Krótkie prośby.
Ograniczam jednoczesne połączenia i aktywnie je mierzę. Zbyt wysokie limity zużywają FD i RAM. Zbyt niskie limity odcinają legalnych użytkowników. Znajduję najlepsze miejsce z czystymi metrykami dla otwartych, bezczynnych, aktywnych i zerwanych połączeń. Ta separacja oszczędza mi globalnego Zwiększenia limitów czasu i chroni Pojemność.
HTTP/2, multipleksowanie i keep-alive
HTTP/2 multipleksuje kilka strumieni za pośrednictwem protokołu Połączenie, ale pozostaje zależny od timeoutów. Utrzymuję umiarkowane okno bezczynności, ponieważ sesje mogą również parkować pod HTTP/2. Wysokie keepalive_requests są tutaj mniej ważne, ale recykling pozostaje przydatny. Blokowanie Head-of-Line przenosi się na poziom ramki, więc nadal mierzę opóźnienie per Strumień. Więcej szczegółowych informacji na temat porównania można znaleźć na stronie Multipleksowanie HTTP/2.
W HTTP/2 zwracam szczególną uwagę na liczbę aktywnych strumieni na połączenie. Zbyt wiele równoległych strumieni może przeciążyć wątki aplikacji. Następnie spowalniam limity lub zwiększam liczbę pracowników serwera. To samo dotyczy tutaj: zmierz, dostosuj, zmierz ponownie. Pozwala to utrzymać Czasy reakcji rzadkie i zachowane Zasoby.
TLS, wznawianie sesji i HTTP/3/QUIC
Uściski dłoni TLS są drogie. Używam Wznowienie sesji (bilety/identyfikatory) i zszywanie OCSP, dzięki czemu ponowne połączenia są szybsze w przypadku zakończenia połączenia. W protokole HTTP/3, QUIC przejmuje warstwę transportową: w tym przypadku Limit czasu bezczynności QUIC podobny do Keep-Alive, ale na bazie UDP. Tutaj również utrzymuję umiarkowane okna i mierzę retransmisje, ponieważ utrata pakietów ma inny wpływ niż w przypadku TCP. W przypadku środowisk mieszanych (H1/H2/H3) wybieram znormalizowane wartości referencyjne i dokonuję drobnych korekt dla każdego protokołu.
Monitorowanie, metryki i testy obciążenia
Ufam danym pomiarowym bardziej niż przeczuciom i zaczynam od jasnych danych. KPI. Ważne są: otwarte gniazda, wykorzystanie FD, nowe połączenia/s, opóźnienia (P50/P90/P99), stopy błędów i retransmisje. Uruchamiam realistyczne profile obciążenia: Warmup, plateau, ramp-down. Następnie porównuję krzywe przed i po zmianie limitu czasu. Spojrzenie na Kolejkowanie serwerów pomaga w jasnej interpretacji czasów oczekiwania.
Dokumentuję każdą regulację za pomocą znacznika czasu i zmierzonych wartości. Pozwala mi to zachować historię i rozpoznać korelacje. Poważnie traktuję negatywne efekty i szybko je cofam. Małe, zrozumiałe kroki oszczędzają dużo czasu. Ostatecznie liczy się stabilność Opóźnienie i niski Wskaźnik błędów pod obciążeniem.
Metody i narzędzia pomiarowe w praktyce
- Szybkie testy: Używam narzędzi takich jak wrk, ab lub vegeta do sprawdzania kwot ponownego użycia (połączenie -H: keep-alive vs. close), połączeń/s i percentyli opóźnień.
- Widok systemu: ss/netstat pokazuje statusy (ESTABLISHED, TIME_WAIT), lsof -p zużycie FD, dmesg/syslog wskazuje spadki.
- Metryki serwera WWW: nginx stub_status/VTS i Apache mod_status dostarczają aktywnych/nieaktywnych/oczekujących i żądań/s. Na tej podstawie mogę rozpoznać szczyty bezczynności lub wąskie gardła pracowników.
- Ślady: Używam rozproszonego śledzenia do monitorowania, czy czasy oczekiwania występują na granicy sieci, czy w aplikacji.
Konfiguracja krok po kroku
Najpierw określam rzeczywisty wzorzec użycia: ile żądań na sesję, które Interwały między kliknięciami, jak duże są odpowiedzi. Następnie ustawiam początkowy profil: timeout 10 s, keepalive_requests 200, umiarkowana liczba pracowników. Następnie przeprowadzam testy obciążenia z reprezentatywnymi danymi. Oceniam liczbę nowych połączeń na sekundę i wykorzystanie FD. Następnie dostosowuję Wartości w 2-3 sekundowych odstępach.
Powtarzam ten cykl, aż opóźnienia pozostaną stabilne pod obciążeniem, a szczyty FD nie osiągną limitu. Przy dużym natężeniu ruchu zwiększam limit czasu tylko wtedy, gdy wyraźnie widzę mniej nowych połączeń, a pracownicy nadal pozostają wolni. Przy niskim wykorzystaniu zmniejszam limit czasu, aby uniknąć bezczynności. W szczególnych przypadkach, takich jak SSE, ustawiam dedykowane bloki serwerów z wyższymi limitami. Ta ścieżka prowadzi do odpornego Ustawienie bez tektury.
Kubernetes, kontenery i automatyczne skalowanie
W środowiskach kontenerowych używam conntrack-limity, limity pod FD i zaległości węzłów. Zapewniam spójne czasy bezczynności między Ingress, siatką usług/proxy i aplikacją. W przypadku automatycznego skalowania zwracam uwagę na Czasy odpływuGdy pody są kończone, powinny odrzucać nowe połączenia poprzez „Connection: close“ i czysto obsługiwać istniejące. Zbyt długie wartości "Keep alive" niepotrzebnie wydłużają drenaż, a zbyt krótkie generują burze uzgodnień podczas skalowania.
Graceful shutdown i rolling deployments
Ja również planuję się wyłączyć. Przed wdrożeniem stopniowo zmniejszam Keep-Alive lub wysyłam ukierunkowane wiadomości. Połączenie: zamknij w odpowiedziach, aby klienci nie otwierali nowych bezczynnych połączeń. W nginx, a worker_shutdown_timeout dla uruchomionych żądań. W Apache używam mechanizmów graceful i pilnuję MaxConnectionsPerChild/Worker, aby recykling odbywał się automatycznie w czasie. Zapewnia to płynność wdrożeń bez twardego ograniczania otwartych gniazd.
Dostrajanie systemu operacyjnego: porty, timeouty, parametry jądra
- porty efemeryczne: Wybierz szeroki zakres dla ip_local_port_range, aby krótkotrwałe połączenia nie napotykały na niedobory.
- TIME_WAIT: Obserwuję szczyty TW. Nowoczesne stosy dobrze sobie z tym radzą; unikam podejrzanych tweaków (tw_recycle).
- tcp_keepalive_time: Nie mylę tego z HTTP Keep-Alive. Jest to mechanizm jądra do rozpoznawania martwych peerów - przydatny za NAT, ale nie zastępujący okna bezczynności HTTP.
- Zaległości i bufory: wymiar somaxconn, tcp_max_syn_backlog i rmem/wmem rozsądnie, aby nie dławić się pod obciążeniem.
Lista kontrolna rozwiązywania problemów
- Wiele nowych połączeń pomimo keep-alive: Zbyt krótki limit czasu lub wcześniejsze odcięcie klientów/LB.
- Wysokie wartości bezczynności i pełne FD: Zbyt długi limit czasu lub zbyt duże pule pracowników w stosunku do wzorca ruchu.
- Błąd RST/Timeout dla dłuższych sesji: NAT/firewall na zbyt krótkiej ścieżce, asymetria między łączami.
- Opóźnienia długiego ogona (P99): Sprawdź limity czasu wysyłania/odczytu, powolnych klientów lub przepełnione zaległości.
- Backendy przeciążone pomimo niskiego obciążenia krawędzi: Brak górnej klatki lub jest ona zbyt mała.
Profile treningowe i wartości początkowe
- API-first (krótkie połączenia): Keep-Alive 5-10 s, keepalive_requests 200-300, ścisłe limity czasu nagłówka/odczytu.
- Handel elektroniczny (mieszany): 8-12 s, 200-400, nieco więcej w przypadku obrazów produktów i buforowania trafień.
- Assets/CDN-like (wiele małych plików): 10-15 s, 300-500, silny nurt i wysokie limity FD.
- Intranet/niskie obciążenie: 5-8 s, 100-200, aby nie dominowała praca na biegu jałowym.
Krótkie podsumowanie
Ustawiłem limit czasu HTTP keep-alive, aby połączenia były ponownie wykorzystywane bez blokowania wątków. W praktyce, 5-15 sekund i 100-500 żądań na połączenie zapewniają bardzo dobre wyniki. Koordynuję limity czasu klienta, load balancera i firewalla, oddzielam długo działające połączenia, takie jak WebSockets i reguluję limity systemu operacyjnego. Dzięki czystemu monitorowaniu, realistycznym testom obciążenia i małym krokom, osiągam niskie wyniki. Opóźnienia i wysoki Przepustowość. Ci, którzy utrzymują tę dyscyplinę, uzyskują wymierną wydajność z istniejącego sprzętu.


