Razvoj

Unpacking the PHP Request Lifecycle for Peak Performance

Razumijevanje životnog ciklusa PHP zahtjeva za vrhunske performanse

Spora PHP aplikacija rijetko ima jedan dramatičan nedostatak. Češće gubi vrijeme u malim, ponovljivim trenucima: pokrećući kod koji zahtjev ne treba, prerano otvarajući veze, učitavajući previše konfiguracije, izvršavajući upite koje je moguće izbjeći ili zadržavajući radnik dok vanjska usluga oklijeva.

Razumijevanje životnog ciklusa zahtjeva čini te troškove vidljivima. Ono rad na performansama pretvara iz skupa folklornih popravaka u praktičnu vježbu: pratite jedan zahtjev od dolaska do odgovora, izmjerite svaku fazu i uklonite posao koji ondje ne pripada.

Započnite putem kojim zahtjev zaista prolazi

U tipičnoj PHP implementaciji web-poslužitelj poput Nginxa ili Apachea prima HTTP zahtjev i prosljeđuje odgovarajuće dinamičke zahtjeve PHP-u. Uz PHP-FPM to obično znači dodjeljivanje zahtjeva dostupnom radničkom procesu. Radnik izvršava ulaznu točku aplikacije, šalje odgovor i postaje dostupan za drugi zahtjev.

Detalji okvira razlikuju se, ali širi životni ciklus je poznat:

  1. Web-poslužitelj izravno poslužuje statičke datoteke ili prosljeđuje zahtjev PHP-u.
  2. PHP radnik počinje obrađivati zahtjev i učitava ulaznu točku aplikacije.
  3. Početni kod učitava konfiguraciju, registrira automatsko učitavanje, inicijalizira usluge i gradi kontekst zahtjeva.
  4. Usmjeravanje i posrednički slojevi odlučuju što treba obraditi zahtjev.
  5. Kod aplikacije čita ili mijenja podatke i može pozvati vanjske usluge.
  6. Aplikacija stvara odgovor, koji posrednički slojevi mogu izmijeniti prije slanja.
  7. Obavlja se čišćenje i radnik se vraća u PHP-FPM skup.

Ovaj slijed je važan jer svaki zahtjev plaća rad izvršen prije nego što se može proizvesti odgovor. Brz kontroler ne može nadoknaditi početnu fazu koja učitava nepotrebne usluge ili sloj baze podataka koji stvara veze za krajnje točke koje nikada ne upituju podatke.

Zadržite statički sadržaj izvan PHP-a

Prva optimizacija je arhitektonska, a ne PHP mikrooptimizacija. Slike, CSS, JavaScript, preuzimanja i javni resursi koji se mogu predmemorirati obično bi trebali biti posluženi putem web-poslužitelja ili CDN-a. Njihovo slanje kroz prednji kontroler troši PHP radnike na posao koji runtime ne mora obavljati.

Pažljivo usmjerite web-poslužitelj tako da samo putanje aplikacije dosežu PHP. Time se smanjuje pritisak na radnike i poboljšava ponašanje dinamičkog prometa tijekom skokova opterećenja. Također čini planiranje kapaciteta jasnijim: ograničenja PHP radnika mogu odražavati stvarne zahtjeve aplikacije, umjesto svakog resursa na koji se stranica slučajno poziva.

Učinite početno pokretanje razmjernim krajnjoj točki

Početni kod lako je previdjeti jer se dijeli. Upravo zato zaslužuje pažnju: mali trošak pomnožen sa svakim zahtjevom može dominirati zauzetom uslugom.

Automatsko učitavanje općenito je pravi zadani izbor, ali izbjegavajte unaprijed učitavati široke skupove klasa „za svaki slučaj”. Slično tome, konfiguraciju treba predmemorirati ili kompajlirati kada okvir to podržava, uz očuvanje pouzdanog procesa implementacije za ponovnu izgradnju te predmemorije nakon promjena konfiguracije.

Spremnici usluga zaslužuju istu disciplinu. Ovisnost se može registrirati bez trenutačnog instanciranja. Preferirajte lijenu izgradnju kada je usluga skupa, a potrebna je samo nekim rutama. Provjera stanja, primjerice, ne bi trebala morati inicijalizirati klijent e-pošte, integraciju pretraživanja ili složenu uslugu izvještavanja.

Cilj nije učiniti svaku ovisnost lijenom. Cilj je osigurati da svaki zahtjev plaća samo mogućnosti koje koristi.

Koristite posrednički sloj kao granicu troška

Posrednički sloj vrijedan je za autentikaciju, zapisivanje, ograničavanje stope, višezakupništvo, lokalizaciju i zaglavlja odgovora. Također može postati nevidljiv porez kada globalni posrednički sloj izvršava upite baze podataka, introspekciju tokena ili udaljene pozive za rute koje ih ne zahtijevaju.

Klasificirajte posrednički sloj prema opsegu:

  • Globalni posrednički sloj trebao bi biti jeftin i univerzalno potreban.
  • Posrednički sloj skupine ruta trebao bi podržavati smislen razred krajnjih točaka.
  • Posrednički sloj specifičan za rutu trebao bi štititi ili obogaćivati samo krajnje točke kojima je potreban.

To je osobito važno za API krajnje točke koje moraju ostati brze pod opterećenjem. Autentikacija može biti neizbježna, ali dohvat autorizacije, konfiguracije zakupca i procjena zastavica značajki ne moraju se svi odvijati prije svake javne krajnje točke.

Vrijeme baze podataka dio je vremena zahtjeva

Većina latencije aplikacije ne troši se na izvršavanje PHP instrukcija. Često se troši na čekanje: baze podataka, predmemorije, mreže, diska ili druge usluge. Baza podataka često je najznačajnija ovisnost jer kod aplikacije može jednu logičku stranicu pretvoriti u desetke upita, a da taj trošak ne učini očitim.

Započnite promatranjem broja upita i trajanja upita po krajnjoj točki. Tražite ponovljene upite unutar petlji, nedostajuće indekse na uobičajenim filtrima i spajanjima te upite koji vraćaju daleko više stupaca ili redaka nego što odgovor treba. Ispravljanje obrasca N+1 upita obično je vrednije od podešavanja male petlje u PHP-u.

Rukovanje vezama također pripada razmišljanju o životnom ciklusu. Stvaranje veze ima trošak, ali zadržavanje previše neaktivnih veza može iscrpiti kapacitet baze podataka. Postavite broj PHP-FPM radnika imajući na umu proračun veza baze podataka. Ako svaki radnik može držati vezu s bazom podataka, maksimalan broj radnika mora biti siguran za bazu podataka, a ne samo za CPU i memoriju.

Predmemorirajte tek nakon što utvrdite ponavljajući skupi posao. Predmemorija može zaštititi bazu podataka od prometa s mnogo čitanja, ali također uvodi invalidaciju, ponašanje zastarjelih podataka i načine kvara. Definirajte što aplikacija treba učiniti kada predmemorija nije dostupna: ne uspjeti sa zahtjevom, vratiti se na bazu podataka ili poslužiti ograničen odgovor. Ta odluka treba biti namjerna.

Zaštitite radnike od sporih ovisnosti

PHP-FPM radnik zauzet je dok zahtjev ne završi. Ako vanjski API zastane, radnik čeka. Uz dovoljno istodobnih sporih zahtjeva skup se može popuniti, a zdravi zahtjevi počinju čekati u redu iza nezdravih.

Svaka odlazna ovisnost treba ograničeno ponašanje. Postavite vremenska ograničenja za povezivanje i ukupno trajanje, izričito obradite neuspjele odgovore i izbjegavajte automatske ponovne pokušaje koji tijekom prekida višestruko povećavaju promet. Kada je ponovni pokušaj primjeren, trebao bi biti ograničen, primijenjen samo na sigurne operacije i osmišljen imajući na umu idempotentnost.

$response = $client->request('GET', $url, [
    'connect_timeout' => 1.0,
    'timeout' => 3.0,
]);

Točne opcije klijenta razlikuju se ovisno o biblioteci, stoga ovo smatrajte obrascem, a ne ugovorom za kopiranje i lijepljenje. Važno je da odlazni poziv ima jasnu gornju granicu i koristan rezervni put.

Za posao koji ne mora biti dovršen prije nego što korisnik primi odgovor, koristite red čekanja ili drugi asinkroni mehanizam. Dostava e-pošte, generiranje minijatura, izrada izvještaja i nekritične obavijesti uobičajeni su kandidati. Neka zahtjev bude odgovoran za provjeru i bilježenje namjere; neka radnik obradi sporiji naknadni zadatak.

PHP-FPM i implementacija značajke su performansi

Konfiguracija runtimea dio je performansi aplikacije. PHP-FPM skup s premalo radnika stvara redove čak i kada stroj ima slobodnih resursa. Previše radnika može uzrokovati pritisak na memoriju, prekomjeran broj veza s bazom podataka i natjecanje za CPU. Pravi broj ovisi o memoriji po radniku, trajanju zahtjeva, obliku prometa i ograničenjima povezanih sustava.

Mjerite prije promjene postavki skupa. Zajedno pratite čekanje zahtjeva u redu, iskorištenost radnika, upotrebu memorije, stope pogrešaka i opterećenje baze podataka. Veći skup može nakratko prikriti sporu ovisnost, a pritom učiniti eventualni kvar širim.

U produkciji omogućite i održavajte PHP-ovu predmemoriju operacijskog koda. Ona izbjegava ponovno parsiranje i kompajliranje PHP izvornog koda pri svakom zahtjevu. Implementacije također trebaju biti osmišljene tako da generirana konfiguracija, predmemorije ruta i metapodaci automatskog učitavanja odgovaraju objavljenom kodu. Predmemorije performansi koje su zastarjele nakon implementacije zapravo su pouzdanosni problemi odjeveni u optimizaciju.

Mjerite životni ciklus, ne samo krajnju točku

Korisna istraga performansi prati zahtjev preko granica: vremenska mjerenja web-poslužitelja, vrijeme izvršavanja PHP-a, upite baze podataka, operacije predmemorije, ponašanje reda čekanja i odlazne pozive. ID-ovi korelacije i strukturirani zapisi olakšavaju rekonstrukciju tog puta, osobito kada naizgled spora krajnja točka zapravo čeka drugi sustav.

Najprije se usredotočite na rute s velikim prometom i velikom latencijom. Uspostavite početno stanje, napravite jednu značajnu promjenu i provjerite i latenciju i ispravnost. Odgovor koji je brz zato što je preskočio autorizaciju, neočekivano vratio zastarjele podatke ili izostavio nuspojavu nije optimizacija.

Performanse su discipliniran dizajn zahtjeva

Najveća poboljšanja PHP performansi dolaze od tretiranja svakog zahtjeva kao proračuna. Poslužujte statičke resurse bez PHP-a. Pokrenite samo ono što ruta zahtijeva. Neka upiti budu namjerni. Ograničite svaku ovisnost. Premjestite nebitan posao s kritičnog puta. Dimenzionirajte radnike prema sustavima o kojima ovise.

Kada je životni ciklus jasan, performanse prestaju biti tajanstvene. O svakom zahtjevu postaje lakše razmišljati, lakše ga je mjeriti i mnogo ga je teže slučajno učiniti skupim.

Portret autora bloga

Mihajlo

Ja sam Mihajlo — programer vođen znatiželjom, disciplinom i stalnom željom da stvorim nešto smisleno. Dijelim uvide, tutorijale i besplatne usluge kako bih pomogao drugima da pojednostave svoj rad i rastu u svijetu softvera i umjetne inteligencije koji se neprestano razvija.