- Lang polling i PHP holder HTTP-anmodninger åbne, hvilket kan blokere knappe PHP-workere og skade skalerbarheden, hvis det implementeres med naive blokeringsløkker.
- Moderne hændelsesdrevne tilgange, omhyggelig konfiguration og optimeringer som batching, caching og hastighedsbegrænsning kan afbøde mange problemer med lange polling-processer.
- Alternativer som WebSockets, SSE og administrerede realtidsplatforme giver ofte bedre ydeevne til scenarier med høj samtidighed end rå PHP long polling.
Hvis du nogensinde har prøvet at integrere "realtids"-adfærd i en klassisk PHP-app, er du sandsynligvis stødt på long polling og dens mærkelige ydeevneproblemer. Det, der lyder som et simpelt trick – at holde en HTTP-anmodning åben, indtil der er noget nyt at sende – rejser pludselig akavede spørgsmål om blokerede PHP-processer, serverhukommelse, timeouts og hvordan i alverden man skal skalere ud over en håndfuld brugere.
Denne guide dykker dybt ned i problemet med long polling i PHP, hvorfor det opfører sig som det gør, hvilke flaskehalse der virkelig betyder noget, og hvad du kan gøre ved dem. Vi vil gennemgå, hvor længe afstemninger virker, hvorfor en naiv sleep()-baseret implementering er ofte værre end short polling, hvordan moderne event loops og webservere kan hjælpe, og hvilke optimeringer der er indsatsen værd. Undervejs vil vi sammenligne long polling med WebSockets og Server-Sent Events, berøre sikkerhed og fejlhåndtering, og se på, hvornår du bør droppe PHP til fordel for noget som Node.js eller en administreret realtidsplatform.
Hvad er long polling, og hvordan fungerer det egentlig?
I sin kerne er long polling blot HTTP, hvor serveren bevidst forsinker svaret, indtil den har noget interessant at sige. I stedet for at klienten hamrer på serveren med få sekunders mellemrum og spørger "noget nyt endnu?", sender klienten en anmodning, som serveren holder åben, og svarer kun, når nye data er klar, eller en timeout er nået.
Den typiske lange afstemningsforespørgsel/svarcyklus ser sådan ud: Når browseren (eller en hvilken som helst klient) foretager en HTTP-anmodning til et særligt slutpunkt, accepterer serveren anmodningen, men sender ikke et svar med det samme, og TCP-forbindelsen forbliver åben, mens serveren venter på nye hændelser eller data.
Hvis der kommer en begivenhed – en chatbesked, en notifikation, en spilopdatering – svarer serveren straks den ventende klient. Klienten behandler den data, og vigtigst af alt, affyrer en ny lang polling-anmodning med det samme, så der altid (eller næsten altid) er én åben forbindelse, der venter på den næste opdatering.
Hvis der ikke sker noget i et stykke tid, udsætter serveren anmodningen for timeout efter en konfigureret grænse og svarer enten med tomme data eller en "ingen opdatering"-nyttelast. Klienten sender stadig en ny anmodning, når den modtager dette timeout-svar, og opretholder den langvarige adfærd gennem gentagne HTTP-kald i stedet for en enkelt uendelig forbindelse.
Så selvom folk taler om en "vedvarende" lang polling-forbindelse, er det teknisk set en løkke af HTTP-anmodninger med forholdsvis lang levetid snarere end én uendelig strøm. Den subtile forskel har stor betydning for PHP, fordi hver af disse anmodninger normalt er knyttet til én PHP-arbejdsproces eller -tråd i hele dens levetid.
Lang polling vs. kort polling i PHP
Kort afstemning er det enklere, gammeldags mønster: klienten beder serveren om nye data efter en fast tidsplan, og serveren svarer med det samme. Du rammer måske /notifications hvert 3-5 sekund, tjek databasen hurtigt, og send ned alt nyt.
Denne tilgang er let at ræsonnere omkring, men frygtelig spild af penge, når de fleste meningsmålinger "intet nyt" returnerer. Du bruger CPU, databaseforespørgsler og netværksoverhead på en konstant strøm af for det meste tomme svar, og brugeren kan stadig se forsinkelser mellem en begivenhed og den næste planlagte afstemning.
Lang polling vender modellen om: færre HTTP-anmodninger, men hver enkelt overlever meget længere, mens serveren venter på en hændelse. I teorien reducerer det HTTP-overhead og forbedrer den opfattede responstid, fordi brugerne modtager opdateringer, så snart de sker, i stedet for at vente på det næste poll-interval.
Fangsten i PHP er, at et naivt langt polling-endpoint blot blokerer en worker i ventetiden. Hvis du holder en forbindelse åben i 300 sekunder ved hjælp af en løkke med sleep(), binder du en PHP-proces eller -tråd, der ellers kunne håndtere mange hurtige, korte afstemningsanmodninger inden for samme tidsramme.
Når man sammenligner de to under belastning, kan et dårligt implementeret langt polling-slutpunkt faktisk håndtere langt færre samtidige klienter end kort polling. For eksempel kan en PHP-FPM-pulje, der nemt kan håndtere tusindvis af bittesmå 5-sekunders afstemninger, gå i stå, hvis hver bruger griber en worker i 300 sekunder ad gangen.
Hvorfor det klassiske PHP lange pollingmønster er problematisk
En meget almindelig PHP-opskrift på long polling ser nogenlunde sådan ud: "øg max_execution_time, luk sessionen, og gentag derefter med en sleep() mens man tjekker for nye data”. Konceptuelt er det ligetil: du forhindrer PHP i at timeout for tidligt, frigør sessionslåsen, så andre anmodninger fra den samme bruger kan køre, og poller derefter for nye beskeder i en løkke i op til ~300 sekunder.
Inde i den løkke kan din kode evt. forespørg databasen eller inspicer en cache i hukommelsen ved hver iteration, og hold pause i et sekund eller deromkring sleep(1) for at undgå at hamre bagenden. Hvis du finder en ny notifikation, bryder du ud af løkken, sender svaret og afslutter scriptet; hvis du når tidsgrænsen uden nye data, sender du et tomt array eller en "ingen opdateringer"-markør tilbage.
På klientsiden, lidt JavaScript (almindeligt via $.ajax() i jQuery eller fetch() i vanilla JS) kalder gentagne gange dette slutpunkt. Når browseren modtager et svar (data eller tomt), venter den måske et par sekunder og sender derefter straks en ny lang afstemningsanmodning, der effektivt kører for evigt, så længe brugeren forbliver på siden.
Selvom dette mønster fungerer for en lille brugerbase, rammer det meget hurtigt hårde begrænsninger, fordi hver ventende anmodning bruger en hel PHP-worker. Med en Apache prefork- eller PHP-FPM-opsætning bruger hver blokeret scriptinstans RAM og ressourcer hele tiden, den er inaktiv, hvilket betyder, at selv 30-40 samtidige klienter kan være mærkbare, og 100+ begynder at blive farlige uden omhyggelig justering.
For at gøre tingene værre er det nemt at misforstå, hvad sleep() opkaldet rent faktisk gør. Fra operativsystemets perspektiv laver din PHP-tråd bogstaveligt talt ikke noget produktivt i dvaletilstand, men den tæller stadig som en live worker, der ikke kan genbruges til andre anmodninger.
Tråde, processer og webservermodellen
For virkelig at forstå problemet med long polling i PHP, skal du se på, hvordan din webserver administrerer forbindelser og arbejdsprocesser. Traditionel Apache prefork genererer flere processer, der hver håndterer én anmodning ad gangen; andre MPM'er eller PHP-FPM bruger puljer af workers med et lignende mønster med én anmodning pr. worker.
Når hver lange polling-klient holder en forespørgsel åben i minutter, udtømmer du hurtigt den begrænsede pulje af PHP-arbejdere. Hver enkelt sidder der stort set inaktiv, blokerer hukommelse og forhindrer yderligere trafik i at blive serveret, når du når det konfigurerede maksimum.
Sammenlign dette med systemer bygget op omkring ikke-blokerende I/O- og event-loops, hvor en enkelt OS-tråd kan jonglere tusindvis af samtidige forbindelser, så længe de fleste af dem er inaktive. I den verden venter "noget" inde i OS-stakken ganske vist, men det er ikke en tung brugerproces pr. forbindelse.
Nginx er et godt eksempel på HTTP-siden: det bruger en event-drevet arkitektur til at administrere et enormt antal åbne forbindelser med meget lidt hukommelsesoverhead. Når du tilslutter PHP til Nginx via FastCGI eller PHP-FPM, kan Nginx holde klientforbindelser i live og sende forespørgsler til en pulje af PHP-arbejdere med en fast størrelse, hvilket giver noget plads, men ikke magisk løser blokerende PHP-scripts.
Derfor er det simple mantra "hver lang afstemning blokerer en tråd" en overforenkling, men stadig smerteligt præcist for mange klassiske PHP-implementeringer. Medmindre du bruger en asynkron runtime eller en anden arkitektur, udføres et typisk PHP-script synkront og vil optage en worker, så længe den eksisterer.
Er Node.js virkelig noget særligt her?
Node.js nævnes ofte som en magisk løsning, fordi den som standard bruger en single-threaded event loop og ikke-blokerende I/O. I stedet for at oprette en tråd pr. forbindelse, holder Node styr på mange åbne sockets samtidigt og udfører kun det faktiske arbejde, når data ankommer, eller en hændelse udløses.
Dette arkitektoniske valg gør det langt nemmere at understøtte et massivt antal inaktive lange polling- eller WebSocket-forbindelser på beskeden hardware. Når der ikke sendes nogen beskeder, er Nodens event-loop stadig aktiv, men fungerer næsten ikke, og RAM-forbruget pr. forbindelse er minimalt sammenlignet med en klassisk PHP worker-per-request-opsætning.
PHP er dog ikke helt udelukket fra dette spil; det har også moderne event loop-biblioteker, der leverer lignende ikke-blokerende adfærd. Projekter som ReactPHP, Amp eller Revolt giver dig en event-drevet runtime i PHP, som kan administrere sockets eller asynkrone opgaver uden at starte en blokerende worker pr. forbindelse.
I praksis kører størstedelen af ældre PHP-applikationer dog stadig på en synkron model med Apache eller PHP-FPM og uden et eventloop. For disse apps er omdømmet "Node er bedre til long polling" for det meste fair, fordi du bliver nødt til at redesigne din PHP-stak betydeligt for at opnå sammenlignelig skalerbarhed.
Hvor lang tid afstemning varer sammenlignet med WebSockets og SSE
Long polling er blot én måde at sende data fra server til klient; WebSockets og Server-Sent Events (SSE) er ofte bedre egnede til moderne realtidsapps. Hver teknik har sine egne afvejninger med hensyn til kompleksitet, muligheder og ressourceforbrug.
WebSockets etablerer en ægte tovejskanal mellem klient og server over en enkelt opgraderet TCP-forbindelse. Når opgraderingshandshaket er fuldført, kan begge sider sende beskeder efter behov uden at gentage HTTP-headerne, hvilket er en kæmpe gevinst for chat-apps, multiplayer-spil, dashboards og ethvert scenarie med hyppige opdateringer.
SSE tilbyder derimod en envejsstrøm af begivenheder fra server til klient over en langvarig HTTP-forbindelse. Det passer fint til notifikationer, live feeds eller ethvert tilfælde, hvor kun serveren behøver at sende data, og klienten sjældent sender information tilbage ud over den oprindelige anmodning.
Long polling ligger et sted midt imellem: det simulerer server push ved hjælp af gentagne HTTP-anmodninger, så du betaler stadig forbindelses- og header-overhead hver gang. Fordelen er, at det fungerer med almindelig HTTP-infrastruktur og standardbrowsere uden ekstra protokoller eller speciel klientsupport.
Mange realtidsbiblioteker som Socket.IO starter faktisk med long polling og opgraderer derefter til WebSockets, når det er muligt, og behandler long polling som en fallback snarere end den primære mekanisme. Det fortæller dig en del om den relative effektivitet af lange polling i stor skala.
Sikkerhedshensyn for lange polling-slutpunkter
Selvom long polling føles som en VVS-detalje på lavt niveau, er hvert long polling-slutpunkt stadig bare en HTTP API-overflade, der skal sikres korrekt. Det første ufravigelige skridt er at servere det udelukkende via HTTPS, så trafikken ikke kan opfanges eller manipuleres undervejs.
Ud over transportsikkerheden har du brug for streng godkendelse og autorisation for alle lange polling-anmodninger. Uanset om du bruger sessionscookies, JWT'er, API-tokens eller OAuth, skal serveren verificere, at hver åben forbindelse svarer til en gyldig bruger, og at brugeren har tilladelse til at modtage den anmodede datastrøm.
Nogle klassiske browsersikkerhedsproblemer, som f.eks. CSRF, kan være mindre relevante, hvis du ikke er afhængig af implicit cookiebaseret godkendelse fra standardformularer, men du skal stadig tænke over din specifikke kontekst. Hvis cookies er involveret, eller hvis endpoint'et kan udløse tilstandsændringer, er anti-CSRF-beskyttelse (tokens, same-site cookies osv.) fortsat vigtig.
Inputvalidering er lige så afgørende for long polling som for enhver anden HTTP API. Parametre som kanal-id'er, bruger-id'er, filtre eller tidsstempler skal renses og valideres på serveren for at undgå SQL-injektion, sti-traversering eller logiske fejl, der kan lække data mellem brugere.
Lang polling åbner også døren for denial-of-service-scenarier, fordi forbindelser bevidst er langlivede og relativt dyre i PHP. Du bør håndhæve fornuftige hastighedsgrænser pr. IP eller pr. konto, begrænse antallet af samtidige forbindelser og anvende timeouts for forbindelser for at holde ressourceforbruget under kontrol.
Kontinuerlig overvågning, logning og periodiske sikkerhedsrevisioner er det sidste lag af forsvaret. Logføring af lange pollingfejl, godkendelsesfejl og usædvanlige forbindelsesmønstre giver dig de data, du har brug for til at opdage misbrug, regressioner eller konfigurationsproblemer, før de eksploderer under reel trafik.
Fejlhåndtering og robusthed i lange polling-processer
Da lang polling er afhængig af mange langvarige forbindelser, er robust fejlhåndtering afgørende for at undgå kaskadefejl eller fastlåste klienter. Start med at indstille en realistisk serverside-timeout for hver anmodning, så en manglende hændelse ikke efterlader forbindelser hængende for evigt.
Når denne timeout nås uden nye data, bør serveren svare med en klar, veldefineret nyttelast. Det kan være en tom liste, en specifik JSON-struktur eller en HTTP 204-status, men den skal være forudsigelig, så klienten kan skelne mellem "ingen data endnu" og reelle fejl.
Ved ægte problemer på serversiden – databaseafbrydelser, interne undtagelser, ugyldige parametre – er det vigtigt at sende nøjagtige HTTP-statuskoder. Koder som 500 (serverfejl), 404 (ressource ikke fundet) eller 401/403 (godkendelsesproblemer) giver klienten mulighed for at reagere passende i stedet for blindt at forsøge igen.
På klientsiden er automatisk gentagelseslogik med eksponentiel backoff næsten obligatorisk for long polling. Hvis en anmodning mislykkes eller får timeout på en uventet måde, bør klienten vente lidt, før den næste sendes, og øge ventetiden ved gentagne fejl for at undgå at en backend, der har problemer, bliver forsinket.
Sundhedstjek og styring af forbindelsestilstand er også en del af et godt design. Hvis klienten registrerer et netværkssvigt eller gentagne serverfejl, kan den skifte til en enklere reservemekanisme som f.eks. short polling eller deaktivere realtidsfunktioner, mens der vises en venlig besked til brugeren.
Endelig skal al denne adfærd kunne observeres: logfejl, timeouts og gentagelsesmønstre, og derefter skal disse logfiler og metrikker bruges til at finjustere timeouts, backoff-strategier og infrastrukturkapacitet. Lange afstemninger, der fejler lydløst, er en af de hurtigste måder at få vrede brugere og udiagnosticerede præstationsproblemer på.
Strategier for skalerbarhed, ydeevne og optimering
Lige fra starten skalerer naiv PHP long polling ikke godt, men der er masser af knapper, du kan dreje på, før du giver helt op på det. Målet er at reducere omkostningerne pr. forbindelse og forbedre udnyttelsen af din infrastruktur.
Et af de mest nyttige tricks er at batch-svar i stedet for at sende én besked pr. HTTP-svar. Hvis der opstår flere hændelser i løbet af et enkelt langt afstemningsvindue, skal du samle dem i en enkelt nyttelast, så du amortiserer HTTP-overhead og reducerer antallet af genforbindelser.
Komprimering kan også gøre en betydelig forskel, især for detaljerede JSON-nyttelaster. Aktivering af Gzip (eller lignende) på webserver- eller PHP-niveau reducerer svarstørrelserne, fremskynder leveringen og reducerer båndbreddeforbruget, hvilket er mærkbart i stor skala.
Caching bør ikke overses: et dedikeret cachelag kan absorbere en masse gentaget arbejde, der ellers ville ramme din database eller mikrotjenester. Hvis mange brugere abonnerer på lignende streams, kan cachelagrede snapshots eller beregnede aggregater dramatisk reducere behandlingstiden pr. anmodning.
På forbindelsessiden er pooling og genbrug primært vigtig for backend-afhængigheder som databaser eller eksterne API'er. At holde databaseforbindelser åbne og genbrugte i stedet for at genoprette forbindelsen ved hver afstemning kan reducere latenstid og CPU-belastning, især under høj samtidighed.
Hastighedsbegrænsning og begrænsning tjener to roller: de beskytter din infrastruktur mod misbrug og hjælper med at holde ydeevnen forudsigelig under belastning. Ved at sætte et loft over, hvor ofte en given bruger eller IP-adresse kan åbne lange polling-forbindelser, reducerer du risikoen for ressourceudmattelse og skaber plads til fair use.
Load balancing på tværs af flere applikationsservere er en anden effektiv løftestang. Distribution af lange polling-anmodninger på tværs af noder forhindrer, at en enkelt maskine bliver et hotspot, især når det kombineres med sticky sessions eller eksterne tilstandslagre til brugerabonnementer.
Asynkron behandling og baggrundsarbejdere bør håndtere alt, der ikke strengt taget er en del af "vent på begivenhed, send begivenhed"-løkken. Meddelelseskøer, jobmedarbejdere og distribuerede systemer gør det muligt for dit lange polling-slutpunkt at forblive tyndt og responsivt, mens tungt arbejde foregår andre steder.
Passende forbindelses- og script-timeouts er de sidste sikkerhedsventiler. Luk inaktive eller fastsiddende forbindelser efter en rimelig periode, og hold dem max_execution_time i overensstemmelse med din lange afstemningslogik og vær eksplicit omkring, hvor længe både serveren og klienten må vente.
PHP-specifikke implementeringstips til long polling
Når man implementerer long polling i almindelig PHP, gør et par konfigurations- og kodningsdetaljer en stor forskel for adfærd og stabilitet. En af de store ringer session_write_close() så tidligt som muligt i manuskriptet.
PHP's standard sessionshandler bruger fillåse, hvilket betyder, at en anmodning, der holder sessionen åben, kan blokere andre anmodninger fra den samme bruger. Lukning af sessionen for at skrive udgivelser, der låses, mens der stadig er læseadgang til sessionsdata, så yderligere parallelle anmodninger ikke står i kø bag den lange afstemning.
Du skal typisk også hæve eller tilsidesætte max_execution_time grænse for at tillade scripts at køre i 60, 120 eller 300 sekunder. Uden den justering kan PHP muligvis afslutte scriptet, før din lange polling-løkke er færdig, hvilket kan føre til forvirrende fejl på klientsiden eller halvt leverede svar.
Inde i løkken skal du være meget forsigtig med, hvor ofte du rammer databasen eller andre backends. En tæt løkke, der forespørger databasen hvert 100 millisekund, vil smelte din database selv under moderat belastning; introducer fornuftige dvaletider, cacher eller push-lignende triggere for at holde systemet responsivt.
Det er også værd at tænke over logføring og metrikker i denne løkke. Spor hvor ofte du afslutter på grund af timeouts i forhold til reelle data, hvor lang den gennemsnitlige ventetid er, og hvor mange samtidige lange afstemninger du betjener, og brug derefter disse tal tilbage til kapacitetsplanlægning og kodejustering.
Husk frem for alt, at hvert blokerende PHP-script er knyttet til en worker, så det er vigtigt at minimere loops, dvaletilstande og unødvendigt arbejde, hvis du vil understøtte mere end en lille håndfuld brugere. For interne værktøjer eller apps med lav trafik kan dette være helt fint; for større apps vil du have brug for en mere robust arkitektur.
Virkelige mønstre, biblioteker og alternativer
Mange udviklere oplever lange polling-processer gennem små demoer: et PHP-script overvåger en tekstfil, og når du redigerer filen, vises ændringen øjeblikkeligt i browseren. Dette er en fremragende mental model: simpel kode, tydelig adfærd og øjeblikkelig feedback.
I en produktionssammenhæng løber det trivielle eksempel hurtigt ind i de begrænsninger, vi har diskuteret, men det illustrerer stadig den grundlæggende protokol. En JavaScript-klient sender en AJAX-anmodning, PHP-scriptet blokerer, indtil filen ændres, eller der opstår en timeout, hvorefter det svarer og gentager processen.
Specifikt for PHP-økosystemer finder du en håndfuld frameworks og mønstre, der forsøger at gøre dette mere håndterbart. Laravel, for eksempel, skubber udviklere hen imod at udsende begivenheder via WebSockets eller administrerede tjenester, selvom du stadig kan oprette lange polling-ruter manuelt.
Uden for PHP findes der modne frameworks i næsten alle sprog, der enten understøtter long polling direkte eller giver pænere abstraktioner omkring realtidskommunikation. Node.js med Express.js eller Socket.IO, Python med Flask- eller Django-kanaler, Java med Spring, Ruby on Rails og .NET med ASP.NET SignalR er alle populære valg.
Nogle platforme skjuler hele skalerings- og forbindelsesproblemet for dig. Administrerede realtidstjenester – inklusive dem, der fokuserer på pub/sub-beskeder og tilstedeværelse – tilbyder klient-SDK'er, kryptering, finjusteret adgangskontrol og global infrastruktur til at håndtere millioner af samtidige forbindelser, så du ikke behøver at opfinde hjulet på ny.
I PHP-verdenen kan man ofte få det bedste fra begge verdener ved at kombinere sin eksisterende applikationslogik med en sådan realtidstjeneste. PHP forbliver ansvarlig for forretningsregler, persistens og API'er, mens den eksterne platform håndterer langvarige forbindelser, fan-out og levering i realtid via long polling, SSE eller WebSockets efter behov.
Andre økosystemer mærker disse realtidsstrategier med navne som Comet, Reverse Ajax, HTTP Streaming, Pushlet eller AJAX long polling. Under motorhjelmen er de alle variationer af den samme idé: at forvandle en fundamentalt anmodnings-/svarprotokol til noget, der føles push-baseret.
For PHP-udviklere, der kæmper med lange polling-problemer, er nøglen ærligt at vurdere deres behov for samtidighed, deres hostingmiljø og hvor meget kompleksitet I er villige til at påtage jer. Nogle gange er en simpel blokeringsløkke nok; andre gange er det bedre at anvende et event-løkkebibliotek, flytte push-data til en dedikeret tjeneste eller skifte en del af din stak til en teknologi, der er bygget til et massivt antal åbne forbindelser.
Når man sætter alle disse dele sammen – HTTP-mekanik, PHP's udførelsesmodel, serverarkitektur, sikkerhed, fejlhåndtering og skalerbare designmønstre – bliver lange pollingstops en mystisk performancekiller og blot endnu et værktøj, man bevidst kan bruge, hvor det giver mening.