...

Modele Apache Worker: porównanie modeli Prefork, Worker i Event

Modele Apache Worker określają sposób, w jaki serwer HTTP Apache równolegle przetwarza żądania i wykorzystuje zasoby - w szczególności za pośrednictwem modułów MPM Prefork, Worker i Event. W tym artykule pokażę, w jaki sposób te trzy modele różnią się od siebie pod względem technicznym, jaki wpływ mają na Wydajność i skalowanie oraz która konfiguracja jest przekonująca w rzeczywistych scenariuszach.

Punkty centralne

Poniższe kluczowe punkty dadzą ci szybki przegląd najważniejszych różnic i decyzji dotyczących trzech MPM; następnie przejdę do bardziej szczegółowych informacji i przedstawię Wiedza praktyczna.

  • PreforkOparta na procesach, wysoka izolacja, wysokie zapotrzebowanie na pamięć RAM.
  • PracownikWątki na proces, dobre skalowanie, wrażliwość na keep-alive.
  • WydarzeniePętla zdarzeń rozdziela połączenie i żądanie, bardzo wydajna.
  • StrojenieStartServers, ThreadsPerChild, MaxRequestWorkers.
  • HTTP/2Działa sensownie z Worker i Event, ale nie z Prefork.

Co kontrolują MPM w Apache

Używam modułów wieloprzetwarzania (MPM) do określenia, czy Apache używa procesów czy wątków dla każdego żądania i w jaki sposób serwer Równoległość zapewnia. Prefork tworzy wiele procesów z jednym wątkiem każdy, Worker tworzy kilka procesów z wieloma wątkami, Event opiera się na Worker i oddziela połączenia od rzeczywistego przetwarzania. Wybór ten ma bezpośredni wpływ na wykorzystanie pamięci, procesora i opóźnienia. Dlatego zawsze biorę pod uwagę sesje, keep-alive, protokoły takie jak HTTP/2 i używane moduły. Jeśli zignorujesz MPM, tracisz wymierne korzyści. Wydajność i ryzyko wąskich gardeł.

Prefork: izolacja procesu i kompatybilność

Prefork koncentruje się na indywidualnych procesach dla każdego zapytania, a tym samym zapewnia silne Izolacja. Jeśli jeden proces ulegnie awarii, inne pozostają nienaruszone - zwiększa to odporność na błędy w przypadku nieczystego kodu lub starych rozszerzeń. Cena: każdy proces ma swój własny narzut, więc zużycie pamięci RAM na równoległe połączenie wzrasta. Przy 100 jednoczesnych żądaniach tworzonych jest 100 procesów, co uważam za akceptowalne tylko przy niskim lub średnim obciążeniu. Używam Preforka głównie wtedy, gdy muszę korzystać z modułów bez bezpieczeństwa wątków lub gdy starsze skrypty CGI wymagają dużego zużycia pamięci. Separacja wymagać.

Worker: Wątki i wysoka równoległość

W modelu roboczym poszczególne procesy wykonują wiele wątków, co zmniejsza zapotrzebowanie na pamięć na żądanie. spadki. Architektura ta pozwala na znacznie większą współbieżność na tym samym sprzęcie i jest odpowiednia dla dużej liczby dostępów. Jednak długie połączenia keep-alive mogą wiązać wątki, a tym samym blokować przepustowość. W czystych, bezpiecznych dla wątków konfiguracjach - na przykład z PHP-FPM - osiągam bardzo dobre wartości RPS z Workerem przy umiarkowanym zużyciu pamięci RAM. Używam Workera, gdy potrzebuję wydajnego, opartego na wątkach Skalowanie i keep-alive są rozsądnie kontrolowane.

Zdarzenie: Nieblokująca strategia keep-alive

Zdarzenie bazuje na modelu worker, ale eliminuje słabość keep-alive dzięki funkcji Pętla zdarzeń. Wątek przetwarza tylko faktyczne żądanie; oddzielny mechanizm jest odpowiedzialny za utrzymanie połączenia. Dzięki temu wątki pozostają wolne, a maszyna przetwarza więcej jednoczesnych sesji z niskim opóźnieniem. Event jest szczególnie imponujący dla połączeń HTTP/2, ponieważ multipleksowanie i długie połączenia działają bez marnowania wątków. W nowoczesnych konfiguracjach zaczynam od Event jako Standardowa podstawa i dostosowywać tylko wtedy, gdy moduły lub starsze wymagania są z tym sprzeczne.

Tabelaryczne porównanie MPM

Poniższa tabela podsumowuje kluczowe różnice, dzięki czemu mogę je zobaczyć na pierwszy rzut oka oceniać który model odpowiada obciążeniu i sytuacji modułu. Przed przełączeniem zawsze sprawdzam bezpieczeństwo wątków wszystkich modułów i oczekiwany czas trwania połączenia. Następnie przypisuję MaxRequestWorkers, ThreadsPerChild i inne limity do dostępnych zasobów. Tabela pomaga mi przyjąć wstępne założenia, ale nie zastępuje testów obciążeniowych w rzeczywistych warunkach. W szczególności w przypadku zdarzeń warto przeprowadzić pomiary z długimi fazami keep-alive i HTTP/2 w celu określenia limitów obciążenia. Zalety widoczne.

MPM Procesy/Wątki Zużycie pamięci RAM niezawodność Typowe zastosowanie
Prefork 1 wątek na proces Wysoki Wysoki (dobra izolacja) Niskie/średnie obciążenie, moduły bez bezpieczeństwa wątków, klasyczne CGI
Pracownik Wiele wątków na proces Średni Średni Wysokie obciążenie ze stosem bezpiecznym dla wątków, np. PHP-FPM
Wydarzenie Wątki + pętla zdarzeń Niski Wysoki Bardzo duże obciążenie, długie połączenia, HTTP/2

Czytam z tabeli: Prefork zdobywa punkty z ekranowanie, Worker dla wydajności i Event dla maksymalnego wykorzystania przy jednoczesnych połączeniach. Używam Event dla nowych projektów, pod warunkiem, że nie ma żadnych niezgodności. Prefork może być nadal przydatny dla stabilnych starszych stosów. Ci, którzy dopiero migrują, często osiągają znaczący postęp dzięki Worker. Ostatecznie wybór pozostaje kwestią Ważenie z modułów, profilu ruchu i sprzętu.

Mierzenie wydajności: Benchmarki i metryki

Bez pomiarów każda decyzja MPM pozostaje Założenie. W testach porównawczych Worker dostarcza do około 50 % więcej żądań na sekundę niż Prefork przy dużym obciążeniu; Event również wzrasta, zwłaszcza podczas długich faz keep-alive. Istnieją wyraźne różnice pod względem pamięci: przy około 1000 jednoczesnych połączeniach, konfiguracje Prefork kończą się w przybliżeniu na 2-4 GB pamięci RAM, Worker na 1-2 GB, a Event zwykle poniżej 1 GB. Sprawdzam nie tylko RPS, ale także czas do pierwszego bajtu, 95/99 percentyl i wskaźniki błędów. Profil obciążenia aplikacji jest kluczowy, ponieważ krótkie, szybkie żądania zachowują się inaczej niż streaming lub WebSockets.

Wyjaśnienie parametrów strojenia: StartServers, ThreadsPerChild, MaxRequestWorkers

Zaczynam od konserwatywnych wartości i zwiększam skalę, aż osiągnę pożądaną wartość. Wykorzystanie spotkać. Dla Prefork ustawiam MaxRequestWorkers na podstawie dostępnej pamięci i rozmiaru procesu; dla Worker i Event planuję ThreadsPerChild i liczbę procesów tak, aby ThreadsPerChild × Processes = MaxRequestWorkers. Upewniam się, że jest wystarczająco dużo bufora, aby szczyty obciążenia nie prowadziły do błędów 503. Czysta wartość StartServers zapobiega niepotrzebnym rozwidleniom w warunkach zimnego startu. Jeśli chcesz zagłębić się w temat, możesz znaleźć podstawową wiedzę na stronie Optymalizacja puli wątków, które można przenieść bezpośrednio do konfiguracji Apache.

Przykład #: Zdarzenie (Debian/Ubuntu)
a2dismod mpm_prefork mpm_worker
a2enmod mpm_event
systemctl restart apache2

# Rozsądne wykorzystanie wątków roboczych
# /etc/apache2/mods-available/mpm_event.conf
ServerLimit 16
StartServers 4
ThreadsPerChild 50
MaxRequestWorkers 800
MaxConnectionsPerChild 0

Następnie sprawdzam efekt za pomocą benchmarków i sprawdzam, czy procesor jest wystarczający praca bez tonięcia w przełącznikach kontekstowych. Jednocześnie monitoruję trendy pamięci RAM, aktywność wymiany i otwarte deskryptory plików. Jeśli kolejki stają się widocznie pełne, ostrożnie zwiększam MaxRequestWorkers lub skracam czasy keep-alive. Jeśli wszystko działa płynnie, tworzę kopię zapasową konfiguracji i dokumentuję Wartości graniczne.

Keep-Alive, HTTP/2 i Thread-Contention

Keep-Alive redukuje handshake TCP, ale może wiązać wątki - szczególnie w przypadku Worker MPM, który umieszcza połączenia bezpośrednio na wątkach. Event rozwiązuje dokładnie ten problem poprzez ustanowienie połączenia za pomocą pętli zdarzeń. rozwija się a wątki tylko do aktywnej pracy. Dlatego w przypadku HTTP/2 używam pracowników lub zdarzeń, ponieważ w przeciwnym razie multipleksowanie jest spowolnione. W praktyce lubię monitorować długość kolejki i sprawdzać, czy „retencja wątków“ jest zauważalna. Wskazówki na ten temat znajdują się w artykule Wątek - uwaga którego używam do bardziej dogłębnych analiz.

Dostosowuję również KeepAliveTimeout do aplikacji, aby nieaktywne połączenia nie miały wpływu na Pojemność nie wiążą się. Idealne ustawienie różni się w zależności od API, klasycznych stron LAMP i frontendów opartych na HTTP/2 z wieloma zasobami. Jeśli jest dużo czasu bezczynności, obniżam limit czasu i nieznacznie zwiększam MaxRequestWorkers. Jeśli spodziewam się wielu krótkich żądań, utrzymuję Keep-Alive na umiarkowanym poziomie, aby zaoszczędzić narzut TCP. Jeśli wystąpią czasy oczekiwania, przełączam się na Event lub ustawiam dodatkowe Wystąpienia do.

Praktyczne scenariusze i wybór odpowiedniego modelu

W przypadku starszych aplikacji z ryzykownymi modułami używam Preforka i korzystam z wysokiej ekranowanie. Przy nowoczesnej architekturze PHP-FPM z wieloma jednoczesnymi połączeniami, Worker zapewnia już bardzo dobre wyniki. Event dodatkowo zmniejsza opóźnienia i skaluje się czysto z długimi sesjami, WebSockets i HTTP/2. Na współdzielonych hostingach lub z niejasnym statusem kodu, jestem bezpieczniejszy z Prefork, podczas gdy zwykle wolę Event na VPS i dedykowanym sprzęcie. Jeśli rozważasz alternatywy dla Apache, możesz znaleźć więcej informacji w kompaktowym dokumencie Porównanie serwerów internetowych dodatkowe pomoce decyzyjne dla Nginx i LiteSpeed, które sprawdzam w zależności od sytuacji.

Wydarzenie opłaca się podczas szczytów ruchu o charakterze burst, ponieważ wątki nie są bezczynne. utrzymywać się. W przypadku aplikacji o dużym obciążeniu procesora ograniczam MaxRequestWorkers, aby nie przeciążać maszyny. Jeśli brakuje pamięci RAM, wyrzucam Prefork i nadaję priorytet Workers/Event. W środowiskach z wieloma dzierżawcami kontenery lub grupy cgroup oddzielają usługi, aby pracownicy/zdarzenia mogli wykorzystać swój potencjał. Ostatecznie pomiar potwierdza, który model we własnym stosie ma najniższą wydajność. Opóźnienie materiały eksploatacyjne.

Konfiguracja na Ubuntu/Debian w praktyce

Specjalnie aktywuję i dezaktywuję MPM, testuję efekt i zachowuję opcje wycofania. gotowy. W Debianie/Ubuntu używam znanych poleceń, a następnie sprawdzam status. Następnie dostosowuję pliki mpm_*.conf i rejestruję zmiany wersji. Przed uruchomieniem symuluję szczyty obciążenia, aby wcześnie rozpoznać zakleszczenia lub wąskie gardła pamięci. Dopiero gdy liczniki błędów i percentyle są prawidłowe, przejmuję kontrolę nad Wartości w produkcji.

# Włącz prefork
a2dismod mpm_worker mpm_event
a2enmod mpm_prefork
systemctl restart apache2

Włącz # Worker
a2dismod mpm_prefork mpm_event
a2enmod mpm_worker
systemctl restart apache2

Włączanie zdarzenia #
a2dismod mpm_prefork mpm_worker
a2enmod mpm_event
systemctl restart apache2

# Monitorowanie
apachectl status
htop
journalctl -u apache2 -f

Równolegle monitoruję dzienniki błędów, aby szybko identyfikować problemy z bezpieczeństwem wątków. Znajdź. W przypadku HTTP/2 sprawdzam, czy protokół jest negocjowany poprawnie i czy konfiguracja TLS jest poprawna. Jeśli występują zauważalne opóźnienia, porównuję na przemian prefork/worker/event i obserwuję rozwój pamięci RAM. Jeśli równowaga nie jest właściwa, dostosowuję KeepAlive, liczbę wątków i limity. Pozwala mi to osiągnąć niezawodne czasy reakcji bez Overbooking.

Bezpieczeństwo wątków i kompatybilność modułów

Najważniejszym wstępnym sprawdzeniem przed przełączeniem z Prefork na Worker/Event jest Bezpieczeństwo nici wszystkich modułów. Klasycznie: mod_php jest historycznie ściśle powiązany z Preforkiem; w nowoczesnych stosach używam zamiast tego PHP-FPM poprzez proxy_fcgi, aby sam Apache mógł skalować się w oparciu o wątki. Moduły filtrów i autoryzacji, samodzielnie napisane moduły lub integracje (np. przetwarzanie obrazu) również muszą być uważane za „bezpieczne dla wątków“. Sprawdzam załadowane moduły, analizuję informacje o wydaniu i przeprowadzam test warunków awaryjnych i wyścigowych pod obciążeniem. W przypadku HTTP/2 obowiązuje następująca zasada: W przypadku Prefork praktycznie nie ma takiej opcji - pracownicy/zdarzenia są Warunek wstępny, aby multipleksacja i priorytetyzacja działały.

Planowanie pojemności: realistyczne obliczanie budżetu pamięci masowej

Nie wymiaruję MaxRequestWorkers „na wyczucie“, ale na podstawie mierzalnych procesów i rozmiarów gwintów. Procedura:

  • Uruchom obciążenie testowe, a następnie zmierz rozmiar zestawu rezydentnego (RSS) dla każdego procesu Apache.
  • Należy wziąć pod uwagę dodatkowy narzut na wątek dla pracowników/zdarzeń.
  • Zaplanuj bufory dla jądra, pamięci podręcznej stron, pamięci podręcznej sesji TLS, bufora dziennika i upstreamów.
# Oszacowanie rozmiaru procesu (przykład)
ps -ylC apache2 --sort:rss | awk '{sum+=$8} END {print "RSS (kB) total:",sum}'
ps -L -p  -o pid,tid,psr,stat,rss,cmd
pmap -x  | tail -n 1 # Suma całkowita na proces

Przykład obliczeniowy: Proces zdarzenia zajmuje 25 MB, wątki wymagają średnio 1 MB. Przy 16 procesach i 50 wątkach daje to w przybliżeniu 16 × 25 MB + 800 × 1 MB ≈ 1,2 GB. Ustawiam MaxRequestWorkers = 800, zostawiam 30-40 % RAM wolnego i skaluję po pomiarze. Jeśli używasz Prefork, po prostu oblicz „Rozmiar procesu × MaxRequestWorkers“ i pozostań Konserwatywny.

Limity systemu operacyjnego, zaległości i deskryptory

Apache może być tak szybki, jak jego platforma bazowa. Regularnie sprawdzam trzy punkty:

  • Deskryptory plików: Wątek/proces otwiera gniazda, pliki i potoki. Zwiększam LimitNOFILE przez systemd i weryfikuję transfer.
  • Zaakceptuj zaległości: W przypadku zrywów połączeń powiększam ListenBacklog i zapewniam odpowiednie backlogi jądra.
  • Dostrajanie gniazda/czasu oczekiwania: Ustaw RequestReadTimeout, Timeout i KeepAliveTimeout specjalnie w celu złagodzenia „powolnych klientów“.
# systemd override
systemctl edit apache2
[Service]
LimitNOFILE=65536

# Parametry jądra (tymczasowe)
sysctl -w net.core.somaxconn=4096

# Apache: zaległości i przekroczenia limitu czasu
Listen 0.0.0.0:443
ListenBacklog 1024
Timeout 60
RequestReadTimeout header=10-20,MinRate=1 body=10,MinRate=500
KeepAliveTimeout 5
MaxKeepAliveRequests 100

Wolę utrzymywać nieco bardziej rygorystyczne limity czasu i monitorować wskaźniki błędów. Jeśli spodziewane jest długie przesyłanie, dostosowuję wartości specjalnie według VirtualHost dalej.

Łaskawe przeładowania, wdrożenia i kontenery

W działaniu preferuję przeładowania bez przerywania istniejących połączeń. apachectl -k graceful lub systemctl reload przeładowuje konfiguracje, ale pozwala na czyste wygasanie uruchomionych żądań - dla prefork na proces, dla worker/event na wątek. W środowiskach kontenerowych planuję mniejszy ServerLimit/ThreadsPerChild, aby strąki mogły być start i wyjść. Zwracam uwagę na limity cgroup: jeśli czas procesora lub pamięć RAM są ograniczone, MaxRequestWorkers musi być odpowiednio niższy, w przeciwnym razie opóźnienie przesunie się do 95/99 percentyla.

Prawidłowo zwymiarowane konfiguracje proxy/upstream

Wiele instancji Apache kończy TLS, a następnie proxy do PHP-FPM, serwerów aplikacji lub mikrousług. Łączę pojemność frontendu (MaxRequestWorkers) z pulami upstream: Dla PHP-FPM, pm.max_children i pm.max_requests są twardym górnym limitem. Utrzymuję taki stosunek, aby Apache nie akceptował znacznie więcej jednoczesnych żądań, niż może obsłużyć upstream - w przeciwnym razie kolejki i Limity czasu. Wyraźnie ustawiam timeouty dla proxy_fcgi i proxy_http i sprawdzam, czy keep-alive jest przydatne dla upstream, czy tylko wiąże zasoby.

Monitorowanie i diagnostyka za pomocą tablicy wyników

Wyjście mod_status ujawnia, jak dobrze działa wybrany MPM. Zwracam uwagę na proporcje następujących statusów: Czytanie (nagłówki przychodzące), Wysyłanie (odpowiedź jest przekazywana), Keepalive (otwarte połączenie bez pracy), Oczekiwanie (bezpłatny). Wysokie proporcje Keepalive w Worker wskazują powiązane wątki - Event dokładnie to eliminuje. Stały Czytanie może wynikać z powolnych klientów lub nieprawidłowego RequestReadTimeout-wartości. Wiele Zamykanie/logowanie-Stany pod szczytowym obciążeniem wskazują na zbyt małe pule wątków lub wąskie gardła we/wy w logowaniu.

Bezpieczeństwo i wytrzymałość: Slowloris & Co.

Połączenie Event-MPM, krótkich KeepAliveTimeout i RequestReadTimeout pomaga w walce z atakami typu „Slowloris“. Chociaż Prefork chroni przed awariami modułów poprzez izolację procesów, pozostaje podatny na RAMWyczerpanie z wieloma połączeniami. Łączę limity na poziomie serwera WWW z upstream WAF / limitami szybkości, aby Apache nie był konfrontowany z milionami półotwartych sesji. Analizuję logi do 95/99 percentyla, ponieważ ataki zawyżają ogony rozkładu.

Domyślne ustawienia dystrybucji i typowe przeszkody

Event jest obecnie standardem w wielu instalacjach Debiana/Ubuntu. Niemniej jednak wartości domyślne są często konserwatywne (np. ThreadsPerChild 25-50). Zwiększam je dopiero po dokonaniu pomiarów. Częste błędy:

  • MaxRequestWorkers wyższa niż liczba dostępnych deskryptorów plików.
  • Niezsynchronizowane limity między serwerami Apache i PHP-FPM/App.
  • KeepAliveTimeout zbyt wysokie dla pracowników z wieloma klientami mobilnymi.
  • Brakujący bufor dla dziennika we/wy - zadania rotacji bloków krótkoterminowy.

Dokumentuję wartości docelowe (wykorzystanie CPU, RAM, RPS, P95) i zapisuję wersję konfiguracji roboczej. Tylko wtedy Roll-out.

Krótkie podsumowanie

Prefork zapewnia silny Izolacja dla starszych stosów, ale kosztuje dużo pamięci. Worker oferuje dobre centrum z wątkami na proces i skaluje się czysto, o ile Keep-Alive nie wiąże się niepotrzebnie. Event oddziela połączenie od przetwarzania, zwiększa wykorzystanie i pokazuje swoją siłę z HTTP/2 i długimi sesjami. Systematycznie mierzę, dostosowuję limity i wybieram MPM, który pasuje do kodu, profilu ruchu i sprzętu. Dzięki czystemu strojeniu, jasnym celom pomiarowym i skoncentrowanemu monitorowaniu, Apache w pełni wykorzystuje każdy z trzech modeli. Wydajność na zewnątrz.

Artykuły bieżące