...

Apache Event MPM vs. Worker MPM: Moderne webserver-turbo til høj belastning

Jeg vil i to sætninger forklare, hvorfor valget af Apache MPM har en mærkbar indflydelse på gennemstrømningen, latenstiden og stabiliteten under høj belastning. Her sammenligner jeg konkret Event MPM og Worker MPM i forbindelse med lange keep-alive-forbindelser, HTTP/2 og høj parallelitet og udleder deraf klare anbefalinger til optimering.

Centrale punkter

For at du straks kan få øje på de vigtigste pointer, opsummerer jeg kort de centrale budskaber og fremhæver de afgørende nøgleord med fed skrift. Ud fra disse punkter udleder jeg længere nede konkrete fremgangsmåder og konfigurationer, som jeg forklarer på en praktisk måde. Jeg vurderer begge MPM’er konsekvent ud fra realistiske belastningsprofiler med mange forbindelser. Så kan du uden omveje se, hvilket modul der udmærker sig i din stack. Listen giver dig en genvej til velunderbyggede beslutninger i den daglige drift.

  • Begivenhed adskiller Idle-Keep-Alive fra anmodningstråde og kan skaleres ved mange forbindelser.
  • Arbejder fungerer godt ved korte anmodninger, men binder tråde ved lang keep-alive.
  • HTTP/2 drager målbar fordel af begivenheden takket være effektiv multiplexing-håndtering.
  • Ressourcer: Event holder RAM/CPU-forbruget pr. aktiv forespørgsel på et lavere niveau.
  • Kompatibilitet: Trådsikre moduler er obligatoriske, mod_php forbliver et Prefork-område.

Hvorfor Worker og Event vinder kapløbet

I en moderne virksomhed satser jeg helt klart på Tråde, fordi de bruger mindre RAM pr. forbindelse end processer. Prefork sikrede tidligere sikkerheden med ikke-trådsikre moduler, men det er svært at skalere ved mange forbindelser. I dag dominerer Worker og Event, fordi de håndterer mange samtidige brugere problemfrit. Det betaler sig især med aktiv Keep-Alive og HTTP/2, hvor forbindelserne forbliver åbne i lang tid. Netop dér viser Begivenhed dets styrker, da det ikke belaster værdifulde request-tråde med tomgangsforbindelser.

Apache Worker MPM: Arkitektur og begrænsninger

Jeg definerer en worker som en hybrid mellem processer og Tråde, hvor hver underproces har en lyttertråd og mange servertråde. En anmodning havner i en tråd, besvares og frigiver derefter tråden igen. Hvis forbindelsen forbliver åben, forbliver den samme tråd bundet til denne forbindelse. Dette medfører inaktivitet, når mange klienter venter længere tid eller kun sporadisk sender små anmodninger. Hvis man anvender Worker, bør man derfor bevidst dimensionere trådpuljer og begrænsninger og kan i den forbindelse læse min korte Optimering af trådpuljen bruge som udgangspunkt.

Apache Event MPM: En forklaring på event-loop’en

Jeg beskriver en begivenhed som en »worker-plus-begivenhedsløkke«, altså Lytter-Tråde, der parkerer inaktive forbindelser. Listeneren accepterer nye forbindelser, videresender aktive anmodninger til ledige arbejdstråde og henter derefter forbindelsen tilbage. På denne måde arbejder anmodningstrådene kun, når der flyder data. Hundredvis eller tusindvis af klienter kan derfor forblive åbne uden at blokere trådene. Netop dette Parkering gør Event så effektivt ved typiske HTTP/1.1- og HTTP/2-arbejdsbelastninger.

Begivenhed vs. arbejdstager: Forskelle under belastning

Jeg vurderer altid begge MPM'er ud fra virkelige Belastning med lange Keep-Alive-tider. Worker når hurtigt grænsen, fordi inaktive forbindelser optager tråde, som der så mangler til nye anmodninger. Event holder trådpuljerne frie og flytter inaktive forbindelser ind i event-loopen. Dermed stiger antallet af brugere, der kan betjenes samtidigt, markant, mens latenstiderne forbliver stabile. Hvis man har brug for et beslutningsgrundlag, er det bedst at sammenligne konkrete hændelsesstyrede servermodeller med trådpuljer i belastningstests.

Kompatibilitet: Moduler og typiske opsætninger

Jeg tjekker først Moduler, da Worker og Event kræver trådsikkerhed. Klassiske mod_php-stacks passer ikke, hvorfor Prefork fortsat giver mening her. Kører PHP derimod via PHP-FPM eller FastCGI, vælger jeg helt klart Event. Det gælder også for reverse-proxyer til app-servere, microservices eller Go/Node-backends. I sådanne opsætninger viser Worker og især Begivenhed sin styrke uden at gå på kompromis med kompatibiliteten.

Konfiguration: De vigtigste direktiver

Jeg vil kort præsentere de vigtigste retningslinjer, så du kan placere dem i den rette sammenhæng og skræddersyet. MaxRequestWorkers begrænser antallet af anmodninger, der behandles samtidigt; ved »Event« kan du ofte sætte værdien højere, da inaktive forbindelser ikke blokerer. ThreadsPerChild definerer antallet af tråde pr. proces; for få tråde nedsætter gennemstrømningen, for mange belaster CPU’en. ServerLimit fastsætter grænsen for processer og dermed den øvre grænse for parallelle anmodninger i systemet. Med KeepAliveTimeout styrer du, hvor længe forbindelser forbliver åbne; jo højere værdien er, desto større er fordelen Begivenhed.

Sammenligning i tabelform: Worker vs. Event

Jeg vil sammenfatte de vigtigste egenskaber i en kortfattet Bord sammen, så du straks kan se forskellene. Den erstatter ikke en belastningstest, men den strukturerer dit overblik over de centrale egenskaber. Læs punkterne fra venstre mod højre, og sæt dem i relation til din trafikprofil. På den måde finder du hurtigt den rette MPM til din arkitektur. Fokus ligger klart på skalering, ressourcebehov og adfærd med Keep-Alive.

Kriterium Worker MPM Begivenhed MPM virkning
Håndtering af Keep-Alive Tråden forbliver bundet til forbindelsen Event-loopen sætter inaktive forbindelser i venteposition Begivenheden holder anmodningstrådene frie
Ressourceanvendelse Flere bundne tråde i tomgang Færre bundne tråde, når systemet er inaktivt Mindre RAM/CPU pr. aktiv forespørgsel
Latenstid under belastning Tag af sted tidligere Forbliver stabil i længere tid Bedre respons
HTTP/2-kompatibilitet Pænt Meget effektiv Fordele ved multiplexing
Konfiguration MaxRequestWorkers, ThreadsPerChild, ServerLimit Straks, plus optimering af event-loop Begivenheden muliggør en højere udnyttelsesgrad
Kompatibilitet Der kræves trådsikre moduler Ligeledes, helst med PHP-FPM Prefork forbliver en mod_php-indstilling

Praksis: Tuning-arbejdsgang og måling

Jeg starter altid med en ren baseline Overvågning og logdata. Derefter ændrer jeg gradvist værdierne for MaxRequestWorkers og ThreadsPerChild og måler latenstid, fejlprocent og CPU-belastning. KeepAliveTimeout tester jeg i trin, da den ideelle tid i høj grad afhænger af klientens adfærd. Herfra er det en god idé at sammenligne Event og Worker med værktøjer som ab, wrk eller JMeter. Først når målingerne ser fine ud, fastlægger jeg Profiler og dokumenter nøgletallene.

Hvornår er Prefork stadig en god løsning?

Jeg bruger Prefork, når det er absolut nødvendigt, at programmet ikke er trådsikkert Moduler skal køre. I så fald er isolering pr. proces vigtigere end skalering. Til gengæld accepterer jeg et betydeligt højere RAM-behov pr. forbindelse. For ældre applikationer, der ikke kan tilpasses, er dette ofte den mest realistiske løsning. Men så snart jeg bruger PHP-FPM eller andre eksterne app-servere, foretrækker jeg Begivenhed tydeligt frem.

Webhosting-sammenhæng og valg af udbyder

I forbindelse med hosting lægger jeg mærke til MPM-profiler, fordi der ofte er mange virtuelle værter på en server løbe. Event giver her den mest effektive udnyttelse af ressourcerne, især med HTTP/2 og TLS. Hvis min stack kræver PHP-FPM, indstiller jeg Event som standard. En kort gennemgang er en hjælp til at få overblik og tjekke den tekniske side Sammenligning af Prefork, Worker og Event inden den endelige afstemning. Den, der laver disse hjemmeopgaver, opnår mærkbart bedre Svartider pr. euro.

Bedste praksis kompakt

Jeg bruger konsekvent PHP-FPM eller andre eksterne app-servere, så Event kan udnytte sit fulde potentiale. Derefter tilpasser jeg MaxRequestWorkers og ThreadsPerChild til CPU-kerner og RAM og tester systemets hårde grænser. Ved mange inaktive klienter vælger jeg Event, indstiller KeepAliveTimeout bevidst højere og overvåger samtidig latenstiderne. Til arbejdsbelastninger med meget korte anmodninger og moderat Keep-Alive er Worker tilstrækkeligt, så længe modulerne forbliver trådsikre. Uden løbende overvågning af trådbelastning, fejl og Forsinkelser træffer jeg ingen endelige beslutninger.

Konkrete konfigurationseksempler for Event og Worker

Jeg leverer to minimalistiske profiler, som jeg bruger som udgangspunkt og derefter finjusterer på baggrund af måleværdier. Det afgørende er: MaxRequestWorkers = ServerLimit × ThreadsPerChild. Jeg regner baglæns ud fra RAM-budgettet og behovet pr. tråd (inkl. moduler, TLS, buffere) og øger gradvist.

# Eksempel: Event MPM (HTTP/2, PHP-FPM)
ServerLimit 16
ThreadLimit 256
ThreadsPerChild 64
MaxRequestWorkers     1024
StartServers 4
MaxConnectionsPerChild 10000

KeepAlive On
MaxKeepAliveRequests  100
KeepAliveTimeout 15

# Valgfrit og skal kun justeres efter måling:
# ListenBacklog 1024
# ThreadStackSize     1048576   # 1 MB, kun hvis modulerne tillader det
# AsyncRequestWorkerFactor 2    # Finjustering af event-loop, normalt bevares standardindstillingen

# HTTP/2
Protocols h2 http/1.1
# H2MaxSessionStreams  100-200  # finjusteres afhængigt af backend-kapaciteten
# Eksempel: Worker MPM (korte anmodninger, moderat Keep-Alive)
ServerLimit 8
ThreadLimit 256
ThreadsPerChild 50
MaxRequestWorkers     400
StartServers 4
MaxConnectionsPerChild 5000

KeepAlive On
MaxKeepAliveRequests  100
KeepAliveTimeout 3
Protocols http/1.1

Jeg holder MaxConnectionsPerChild (Alias: MaxRequestsPerChild) skal være forskellig fra 0 for at opfange snigende lækager. KeepAliveTimeout Jeg indstiller den bevidst højere for »Event«, fordi inaktive forbindelser er billige; for »Worker« holder jeg den lav for ikke at blokere tråde.

Finjustering af HTTP/2 med Event

Jeg tager højde for ved HTTP/2, at browsere åbner få forbindelser og mange Streams multiplexing. Dermed flyttes flaskehalsen fra antallet af forbindelser over til en retfærdig fordeling af tråde og backend-kapacitet. Med »Event« forbliver trådene ledige, så længe en stream venter; det udjævner spidsbelastninger i latenstiden. Praktiske indstillingsmuligheder:

  • H2MaxSessionStreams: Jeg ligger typisk i området 50–200. Er værdien for høj, skaber det »head-of-line«-effekter i backend, er den for lav, går paralleliteten til spilde.
  • MaxRequestWorkers: Med Event kan jeg øge belastningen, forudsat at RAM og CPU kan klare det. Jeg overvåger den 95. og 99. percentil for latenstiden, når parallelismen øges.
  • TLS: Med ALPN og moderne krypteringssuiter reducerer jeg håndtryksomkostningerne; Event drager desuden fordel af, at inaktivitetsfaser mellem stream-bursts udnyttes effektivt.

Operativsystembegrænsninger og socket-backlogs

Før hver belastningstest tjekker jeg systemets grænser, ellers er det ikke MPM, men kernen, der sætter begrænsningen. Ved et stort antal forbindelser skalerer jeg især:

  • Filbeskrivelser: ulimit -n og systemd LimitNOFILE Jeg øger f.eks. til 65536 eller højere; Apache har brug for FD pr. socket, log og pipe.
  • Efterslæb: net.core.somaxconn og tcp_max_syn_backlog Jeg indstiller den passende værdi (f.eks. 1024–4096), så Accept-Queue ikke løber over.
  • Portområde (ved reverse-proxy): ip_local_port_range Jeg udvider (f.eks. 10.000–65.000), hvis der er mange samtidige udgående forbindelser til backend-systemerne.
  • FIN/Timeouts: Vær forsigtig med tcp_fin_timeout: Hvis man er for aggressiv, kan det medføre afbrydelser i forbindelsen; jeg ændrer kun ud fra måleresultaterne.

Jeg dokumenterer hver eneste justering af kernen sammen med begrundelsen og verificerer den ved at foretage en ny belastningsmåling. Uden dokumentation er standardindstillingen som regel den rigtige.

Overvågning og fejlfinding i hverdagen

Jeg aktiverer Udvidet status og brug server-status til at Resultattavle-tilstande. Under „Event« ser jeg mange idle-/keep-alive-sockets, uden at arbejdstrådene er fuldt udnyttet. I fejlloggen vises »server reached« MaxRequestWorkers “setting, overvej at hæve indstillingen MaxRequestWorkers« – reagerer serveren allerede på grænsen; jeg øger værdien forsigtigt og holder øje med RAM/CPU samt fejlprocenten.

  • Målefelter: I adgangslogfilerne registrerer jeg responstider (f.eks. %D/%T), statuskoder og byte; jeg sammenholder spidsbelastninger med CPU/IO.
  • Symptomer hos Worker: Mange inaktive Keep-Alive-forbindelser, tråde ved 100 % optaget, stigende latenstid, 503/504 – tegn på optagede tråde.
  • Symptomer ved en begivenhed: Lyttertråde er stærkt belastede, men arbejdstråde er ledige – oftest en begrænsning i netværket eller backenden, ikke MPM.
  • Graceful-Reload: Jeg implementerer ændringer apachectl -k yndefuld så de eksisterende forbindelser kan løbe frit.

Kapacitetsplanlægning: Fra kerner og RAM til MaxRequestWorkers

Jeg regner pragmatisk: Hvor meget RAM pr. tråd plus buffer vil jeg tillade? Med TLS, filtre og almindelige moduler regner jeg konservativt med nogle MB pr. tråd. Derefter indstiller jeg MaxRequestWorkers således at spidsbelastningen i 95./99.-percentilerne håndteres uden swap. På CPU-niveau gælder det, at tråde ud over antallet af kerner kun er en fordel, så længe de ikke konstant er ressourcekrævende. Med Event tør jeg satse på højere værdier, fordi inaktivitetsfaser næsten ikke koster noget.

  • Tommelfingerregler: Start med 32–64 tråde pr. proces, 4–16 processer; derefter måling og justering.
  • ThreadStackSize: Hvis der er mangel på RAM, og modulerne tillader det, reducerer jeg stakstørrelsen (forsigtigt, med stresstest).
  • MaxKeepAliveRequests: Jeg beholder som regel standardindstillingen; ved snakkesalige klienter kan en højere værdi reducere overhead.

Reverse-proxy-scenarier og backend-forbindelser

Jeg foretrækker især at bruge Event i forbindelse med app-backends, fordi det Frontend-stik parkerer effektivt, mens selve arbejdet foregår i backend. Det afgørende er så samlingen af Backend-forbindelser (mod_proxy):

  • Keep-Alive til backend: Forbliver aktiv for at spare på håndtryk; størrelsen på puljerne (max (pr. mål) i overensstemmelse med backend-kapaciteten.
  • Proxy-timeouts: Definer timeout-værdier klart, så fastlåste backend-processer ikke binder frontend-tråde.
  • HTTP/2 til backend: Hvor det er muligt, bruger jeg H2 (f.eks. internt h2c) for at få færre forbindelser ved flere streams – Event fungerer godt sammen med det.

Jeg holder nøje øje med latensfordelingen mellem frontend og backend; hvis det kun er backend-tiden, der stiger, hjælper MPM-optimering alene ikke – så er jeg nødt til at justere poolstørrelser, timeouts eller backend-ressourcer.

Rollout-strategi og overgang fra Worker til Event

Jeg migrerer i klare trin: Først tjekker jeg Modulliste (apachectl -M) for at kontrollere trådsikkerheden. Alt, der ikke er trådsikkert (f.eks. mod_php), skal fjernes eller isoleres. Derefter aktiverer jeg Event, indstiller konservative startværdier og kører belastningstests på staging-miljøet. Ved udrulningen starter jeg med en delmængde af trafikken (Canary), sammenligner målinger og ruller først derefter bredt ud.

  • kommandoer: Skift mellem MPM-moduler som normalt (f.eks. a2dismod/a2enmod) og genstart systemet korrekt.
  • Reserveplan: Jeg har en arbejdsprofil klar, hvis et modul under »Event« skulle opføre sig mistænkeligt.
  • Dokumentation: Jeg dokumenterer alle ændringer af grænseværdier, HTTP/2-parametre og kerneværdier ved hjælp af målinger før og efter.

Fokus på sikkerhed og TLS-ydeevne

Når det gælder TLS, er jeg opmærksom på, at håndtryk er CPU-krævende og kan øge latenstiden under belastning. Med Genoptagelse af sessionen og ved at vælge moderne krypteringsalgoritmer reducerer jeg omkostningerne, samtidig med at jeg effektivt udnytter inaktive perioder. I kombination med HTTP/2 og ALPN undgår jeg unødvendige roundtrips. Vigtigt: TLS-buffere og OpenSSL-parametre indgår i RAM-forbruget pr. tråd – jeg tager højde for dem i kapacitetsplanlægningen.

Fejltolerance og »graceful degradation«

Jeg planlægger med henblik på overbelastning: Er CPU’en fuldt udnyttet, eller når Apache MaxRequestWorkers, vil jeg ikke have en lavine af gentagne forsøg. Jeg indstiller klare timeouts, informative fejlsider og rate-limits på de forudgående proxyservere. Med Event forbliver flere under pres Tråde til rådighed til egentligt arbejde, mens inaktive forbindelser sættes i venteposition – netop denne reserve sikrer, at systemet kan holdes i drift længere, indtil belastningen falder igen, eller den automatiske skalering træder i kraft.

Kort opsummeret

I den daglige drift satser jeg på Begivenhed, så snart min stack bruger trådsikre moduler og PHP-FPM. Denne tilgang reducerer antallet af bundne tråde ved inaktive forbindelser, holder responstiden stabil og øger antallet af brugere, der betjenes parallelt. Worker er fortsat et solidt valg til korte anmodninger med moderat keep-alive, når Event af organisatoriske årsager ikke passer. Prefork forbeholder jeg til opsætninger med ikke-trådsikre moduler eller gammel kode. Med klare belastningstests, præcis finjustering af direktiverne og synlig Overvågning får jeg Apache til at køre med turbohastighed på en måde, der kan gentages.

Aktuelle artikler