Скриената моќ на PHP: Архитектура за отпорни, скалабилни задни системи
PHP често се оценува според најмалку дисциплинираниот код напишан со него. Тоа е чуден стандард за јазик што може подеднакво ефикасно да напојува чист API, захтевен трансакциски тек на работа и добро изолиран сервис, како што може да напојува и заплеткана скрипта.
Вистинското прашање не е дали PHP може да скалира. Туку дали бекендот е дизајниран да откажува грациозно, безбедно да еволуира и обичниот пат да го прави лесен за одржување. Тоа се архитектонски проблеми. PHP има особено практичен одговор за нив: градете околу јасни граници, потпирајте се на едноставна инфраструктура и одржувајте ја оперативната сложеност пропорционална на проблемот.
Почнете со граници, а не со рамки
Рамката може да ја забрза испораката, но не треба да стане местото каде што живее секоја деловна одлука. Најотпорните PHP апликации рано ги раздвојуваат одговорностите: обработката на HTTP, случаите на употреба на апликацијата, доменските правила и инфраструктурната интеграција треба да имаат различни одговорности.
Контролерот треба да преведе барање во повик до слојот на апликацијата и да го преведе резултатот назад во одговор. Не треба да содржи правила за цени, циклуси за повторни обиди или директни барања до базата на податоци. Таа воздржаност се исплаќа кога истиот случај на употреба подоцна треба да се извршува од worker за редица, задача од командна линија или друг API.
final class CreateOrderController
{
public function __invoke(CreateOrderRequest $request): JsonResponse
{
$order = $this->createOrder->handle(
new CreateOrderCommand(
customerId: $request->customerId(),
items: $request->items()
)
);
return new JsonResponse(['id' => $order->id()], 201);
}
}
Корисната граница тука не е самиот контролер. Тоа се CreateOrderCommand и случајот на употреба зад него. Тоа му дава на системот стабилен центар, додека веб-рутите, потрошувачите на редици и имплементациите на бази на податоци можат да се менуваат на рабовите.
Дизајнирајте API за несовршени мрежи
Бекенд-инженерите поминуваат голем дел од времето справувајќи се со услови што среќниот пат ги игнорира: клиентите повторно испраќаат барања, врските неочекувано се затвораат, а зависните сервиси одговараат бавно. Доверлив API го признава ова уште од почетокот.
За операции што создаваат ресурс или активираат надворешен несакан ефект, поддржете идемпотентност таму каде што е важна. Клиент што повторно испраќа барање поврзано со плаќање по истекување на времето не смее случајно да создаде две наплати. Клуч за идемпотентност, зачуван со исходот од првичното барање, му овозможува на серверот да го врати истиот резултат за еквивалентен повторен обид.
Грешките при валидација треба да бидат јасни и структурирани. Неуспесите при автентикација и авторизација треба да можат да се разликуваат. Неочекуваните неуспеси не треба да откриваат детали за имплементацијата. Доследните облици на одговор им помагаат на повикувачите да изградат доверливо однесување без обратно инженерство на секоја крајна точка.
- Користете соодветни статусни кодови, но направете го и телото на одговорот корисно.
- Поставете експлицитни временски ограничувања за појдовни HTTP повици.
- Повторувајте само неуспеси што веројатно се привремени.
- Користете ограничени повторни обиди со постепено зголемување на чекањето; неограничените повторни обиди претвораат минлив проблем во проблем со ресурси.
- Забележете идентификатор за корелација за едно барање да може да се проследи низ логовите и асинхроната работа.
Повторните обиди заслужуваат посебно внимание. Повторувањето на безбедно читање по краток неуспех на врската може да биде разумно. Повторувањето на неидемпотентно запишување без стратегија за дедупликација може да биде опасно. „Обиди се повторно“ не е стратегија за отпорност, освен ако семантиката на операцијата го поддржува тоа.
Дозволете базата на податоци да ги наметне важните вистини
Кодот на апликацијата треба да ги изразува деловните правила, но базата на податоци треба да ги штити инваријантите што мора да важат при конкурентност. Ако адресата на е-пошта мора да биде единствена, користете уникатно ограничување. Ако записот на дете не може да постои без својот родител, користете странски клуч кога моделот на податоци го поддржува тоа. Ако неколку промени мора да успеат или да не успеат заедно, користете трансакција.
PHP обработувачите на барања често се краткотрајни, што може да направи конкурентноста лесно да се потцени. Две барања сепак можат да ја прочитаат истата достапна залиха и и двете да се обидат да ја резервираат. Решението не е оптимистичка if изјава. Може да бара трансакција, условно ажурирање или стратегија за заклучување избрана за текот на работата.
UPDATE inventory
SET available = available - :quantity
WHERE product_id = :product_id
AND available >= :quantity;
Ако ова ажурирање не засегне ниту еден ред, резервацијата не се случила. Тоа е конкретен резултат отпорен на трки со кој апликацијата може да постапи. Точната шема и пристапот кон изолација ќе се разликуваат, но принципот е траен: наметнувајте критични премини на состојби блиску до податоците.
Користете редици за да го заштитите времето на одговор
Не секое дејство припаѓа во циклусот барање–одговор. Испраќањето известувања, генерирањето извештаи, обработката на прикачувања и повикувањето бавни услуги од трети страни се вообичаени кандидати за асинхрона работа. Преместувањето во редица може да ги одржи API-јата одзивни и да го ограничи опсегот на влијание од бавна зависност.
Но редицата не е магичен слој за доверливост. Worker може да ја прими истата порака повеќе од еднаш. Задача може да откаже на половина пат. Надворешен добавувач може да прифати барање додека worker го губи одговорот. Затоа обработувачите на задачи треба да бидат идемпотентни, набљудливи и безбедни за повторен обид.
За промени што и запишуваат во базата на податоци и објавуваат порака, разгледајте шема outbox. Зачувајте го наменетиот настан во истата трансакција на базата на податоци како и деловната промена, а потоа одделен процес нека го објави. Ова го избегнува кревкиот јаз каде што потврдувањето на базата на податоци успева, но објавувањето на пораката не успева.
Docker треба да ја направи локалната работа предвидлива
Контејнерите се највредни кога ја отстрануваат нејасноста во околината. PHP апликацијата треба да може да работи со истото декларирано извршно опкружување, екстензии и зависности од услуги низ развојните, тестните и продукциските околини.
Одржувајте ги сликите мали и намерни. Раздвојте ги зависностите за градење од зависностите за извршување кога е соодветно. Извршувајте ја апликацијата со конфигурација обезбедена преку механизми специфични за околината, наместо да вградувате тајни во слика. Третирајте ги миграциите на базата на податоци како намерен чекор при распоредување, а не како случаен несакан ефект од секое стартување на контејнер.
Корисното локално поставување обично ги вклучува апликацијата, база на податоци и само придружните услуги што навистина им се потребни на развивачите. Локалното репродуцирање на секоја продукциска интеграција може да создаде повеќе триење отколку сигурност. Претпочитајте брза повратна информација, документирани стандардни поставки и лесен пат за тестирање на вистинската интеграција кога тоа е важно.
Мерете пред да подесувате
Работата на перформансите е најефикасна кога започнува со прашање: што всушност е бавно и при какво оптоварување? Бавна крајна точка може да биде предизвикана од барање без индекс, прекумерна серијализација, повторени оддалечени повици, неефикасна пагинација или едноставно од барање што врши работа што припаѓа во редица.
Прво инструментирајте ги границите. Следете го времетраењето на барањата, стапката на грешки, однесувањето на барањата кон базата на податоци, латентноста на редицата и неуспесите на зависностите. Логовите треба да обезбедат контекст без да станат место за исфрлање чувствителни податоци. Метриките идентификуваат обрасци; трагите и структурираните логови помагаат да се објаснат поединечните неуспеси.
Кеширањето е вредно кога има јасен сопственик и приказна за инвалидирање. Кеширајте податоци со многу читања со познато барање за свежина. Не користете кеширање за да скриете неограничено барање или збунувачки модел на податоци. Најбрзиот повик до базата на податоци сè уште е оној што повеќе не ви треба, но точноста е пред досетливоста.
Направете ја промената првокласно барање
Скалабилноста не е само за справување со повеќе сообраќај. Таа е и за дозволување повеќе развивачи, повеќе функции и повеќе оперативни барања без секое издание да се претвори во коцкање. Малите модули, експлицитните интерфејси, автоматизираните тестови околу деловните правила и повратните промени во базата на податоци ја прават идната работа помалку ризична.
Скриената моќ на PHP е неговата способност да остане блиску до проблемот. Не бара сложена платформа пред тимот да може да изгради дисциплиниран бекенд. Со јасни граници, гаранции поткрепени со база на податоци, промислена асинхрона обработка и работа на перформансите водена од докази, PHP станува она што добра бекенд-технологија треба да биде: доволно сигурна што корисниците никогаш не мора да размислуваат за неа.