Izvan mikroservisa: projektiranje za stabilnost distribuiranih sustava
Mikrousluge se često predstavljaju kao neizbježan sljedeći korak nakon monolita: podijelite aplikaciju, neovisno implementirajte usluge i omogućite timovima brže kretanje. Privlačnost je stvarna. Kao i skriveni trošak.
Distribuirani sustav ne postaje jednostavniji zato što njegov kod živi u manjim repozitorijima ili spremnicima. Postaje mreža djelomičnih kvarova, odgođenih poruka, nekompatibilnih ugovora, dupliciranih podataka i operativnih odluka koje moraju ostati razumne pod pritiskom. Dobra arhitektura manje se svodi na odabir „mikrousluga”, a više na odabir najmanje količine distribucije koja rješava stvaran problem.
Počnite s granicama, a ne jedinicama implementacije
Najjači razlog za izdvajanje usluge nije to što je modul narastao. Razlog je to što ima zasebnu poslovnu granicu, životni ciklus, profil skaliranja, sigurnosni položaj ili model vlasništva.
Primjerice, tijek obrade narudžbe može sadržavati dohvat kataloga, određivanje cijena, autorizaciju plaćanja, rezervaciju zaliha, isporuku i obavijesti. Te sposobnosti ne zaslužuju automatski zasebne usluge. Najprije se zapitajte trebaju li neovisna izdanja, neovisnu pohranu ili neovisno rukovanje kvarovima. Ako je odgovor ne, njihovo zadržavanje zajedno može biti otporniji izbor.
Modularni monolit često je izvrsna prijelazna arhitektura. Omogućuje jasne unutarnje granice bez neposrednog zahtijevanja mrežnih poziva za uobičajenu suradnju. U PHP aplikaciji to može značiti organiziranje koda oko domenskih modula, a ne samo tehničkih slojeva:
src/
Orders/
Application/
Domain/
Infrastructure/
Inventory/
Application/
Domain/
Infrastructure/
Payments/
Application/
Domain/
Infrastructure/
Svaki modul može izložiti usko sučelje na razini aplikacije. Drugi moduli ne bi trebali posezati za njegovim modelima postojanosti ili tablicama kao prečacem. Ta disciplina omogućuje kasnije izdvajanje, ali također poboljšava monolit već danas.
Svaki udaljeni poziv odluka je o pouzdanosti
Unutar jednog procesa poziv funkcije brz je i obično pouzdan. Preko mreže može isteći vrijeme čekanja nakon što je udaljeni sustav dovršio posao. Može ne uspjeti prije primanja odgovora. Može se ponoviti i stvoriti dupliciranu operaciju. To su uobičajeni ishodi, a ne rubni slučajevi.
Razmotrite API za naplatu koji poziva uslugu plaćanja. Naivna implementacija istek vremena čekanja tretira kao neuspjeh i odmah pokušava ponovno. No prvi je zahtjev možda stigao do pružatelja plaćanja i uspio. Ponovni pokušaj mogao bi dvaput autorizirati kupca ako operacija nije idempotentna.
Za naredbe koje mijenjaju stanje prihvatite ključ idempotentnosti i pohranite rezultat povezan s njim. Ponovljeni zahtjev s istim ključem trebao bi vratiti izvorni ishod umjesto da ponovno izvrši radnju.
$key = $request->getHeaderLine('Idempotency-Key');
if ($existing = $idempotencyStore->find($key)) {
return $existing->toResponse();
}
$result = $paymentService->authorize($command);
$idempotencyStore->save($key, $result);
return $result->toResponse();
Ovaj primjer i dalje zahtijeva pažljiv dizajn transakcija: pohranjeni odgovor i poslovna radnja ne smiju se razići ako se proces sruši. Poanta nije univerzalni isječak; već prepoznavanje da ponovni pokušaji zahtijevaju definiran semantički ugovor.
Promišljeno koristite vremenska ograničenja, ponovne pokušaje i redove
Ponovni pokušaji korisni su samo kada je vjerojatno da je neuspjeh privremen i kada operacija može podnijeti ponavljanje. Postavite kratka, izričita vremenska ograničenja. Pokušajte ponovno ograničen broj puta uz postupno produljivanje razmaka. Ne pokušavajte ponovno u slučaju neuspjeha validacije, autorizacije ili zahtjeva koji nisu idempotentni.
Za rad koji nije trenutan red često stvara zdraviju granicu od sinkronog HTTP-a. Slanje e-poruke s potvrdom narudžbe ne bi smjelo usporavati uspješnu naplatu. Objavljivanje događaja kao što je OrderPlaced omogućuje nizvodnim radnicima neovisnu obradu obavijesti i analitike.
Ali redovi ne uklanjaju složenost. Potrošači mogu primiti poruku više puta, poruke mogu stići kasno, a obrada može opetovano ne uspjeti. Izgradite potrošače tako da budu idempotentni, šaljite otrovne poruke na put neuspjeha koji se može pregledati te pratite starost reda kao i njegovu duljinu.
Vlasništvo nad podacima mjesto je gdje arhitektura postaje stvarna
Najštetniji prečac u distribuiranim sustavima jest zajednička baza podataka. Djeluje učinkovito jer svaka usluga može dohvatiti podatke koji su joj potrebni. U praksi povezuje implementacije, otkriva unutarnje pretpostavke i čini promjene sheme rizičnima.
Usluga bi trebala posjedovati podatke koje zapisuje. Druge usluge trebale bi koristiti njezin API, konzumirati njezine događaje ili održavati vlastiti model čitanja. To može uvesti konačnu konzistentnost, stoga korisničko iskustvo to mora iskreno priznati. „Vaša se narudžba potvrđuje” bolje je nego prikazati nepovratan status prije dovršetka zaliha i plaćanja.
Ne zahtijeva svaki upit međuservisni zahtjev u stvarnom vremenu. Izvještavanje, pretraživanje i nadzorne ploče često bolje funkcioniraju s modelima čitanja izgrađenima iz događaja. Kompromis su svježina i operativno opterećenje, što bi trebalo biti izričito, a ne slučajno.
- Držite transakcijske podatke blizu usluge koja posjeduje poslovnu odluku.
- Objavljujte stabilne događaje o činjenicama koje su se dogodile, a ne interne zapise oblikovane prema bazi podataka.
- Verzionirajte API-je i ugovore događaja kada promjene nisu kompatibilne unatrag.
- Planirajte usklađivanje za tijekove rada u kojima privremena nedosljednost ima financijski učinak ili utječe na korisnike.
Spremnici standardiziraju isporuku, a ne dizajn sustava
Docker je vrijedan zato što čini ovisnosti tijekom izvođenja ponovljivima. Ne čini aplikaciju neovisno implementabilnom, promatranom ili sigurnom za rad. Distribuirani sustav u spremnicima i dalje treba upravljanje konfiguracijom, provjere zdravlja, zapisnike, metrike, praćenje i pouzdan put za migracije sheme.
Neka slike budu male i predvidljive, pokrenite jedan primarni proces po spremniku i omogućite smisleni krajnji endpoint za provjeru zdravlja. Endpoint za provjeru zdravlja trebao bi razlikovati „proces je pokrenut” od „aplikacija može opslužiti potreban promet”. Izbjegavajte da ovisi o svakoj neobaveznoj nizvodnoj integraciji, jer bi privremeni prekid obavijesti mogao uzrokovati nestanak zdravih instanci naplate.
Migracije baze podataka zaslužuju posebnu pažnju. Automatsko pokretanje migracija iz svakog web-spremnika stvara rizik od utrka tijekom postupne implementacije. Migracije tretirajte kao izričit korak implementacije, učinite ih kompatibilnima unatrag gdje je moguće i upotrijebite slijed proširivanja i sažimanja: dodajte nove strukture, implementirajte kod koji podržava oba oblika, migrirajte podatke, a zatim kasnije uklonite zastarjele strukture.
Učinite neuspjeh vidljivim prije nego što postane zagonetan
Kada zahtjev prolazi kroz nekoliko usluga, zapisnici iz jednog procesa rijetko su dovoljni. Prenosite identifikator korelacije kroz dolazne zahtjeve, odlazne pozive, asinkrone poruke i strukturirane zapisnike. Zabilježite operaciju, ishod, latenciju i relevantne identifikatore bez otkrivanja tajni ili osobnih podataka.
Mjerite ponašanje koje korisnici doživljavaju: stopu pogrešaka, latenciju, kašnjenje reda, zasićenost i uspjeh ključnih poslovnih tokova. API koji vraća HTTP 200 nije nužno zdrav ako se narudžbe neobrađene gomilaju u redu.
Operativna jednostavnost značajka je. Manje pokretnih dijelova znači manje nadzornih ploča, upozorenja, vjerodajnica, putova implementacije i načina otkaza. Tim koji ne može pouzdano dijagnosticirati uslugu u 2 ujutro nije dobio mnogo njezinim izdvajanjem.
Mirna arhitektura obično je bolja
Mikrousluge mogu biti pravi odgovor kada se neovisni timovi i doista neovisne domene trebaju razvijati različitim brzinama. One nisu oznaka zrelosti. Zrelo inženjerstvo znači znati koja složenost donosi stvarnu sposobnost, a koja samo premješta poziv funkcije na mrežu.
Započnite s jasnim domenskim granicama, osigurajte vlasništvo, dizajnirajte svaku udaljenu operaciju za djelomični neuspjeh i izdvajajte usluge samo kada korist nadmašuje operativno opterećenje. Cilj nije dijagram s više okvira. Cilj je sustav koji ostaje razumljiv, promjenjiv i miran kada se stvarni svijet ponaša nesavršeno.