Престанете да бркате подобрувања на перформансите: Архитектирајте за предвидлива брзина на развој на бекендот
Најскапиот проблем со перформансите често не е бавно барање или преоптоварен worker. Тоа е backend што никој не може безбедно да го промени.
Тимовите можат да поминат недели намалувајќи милисекунди од endpoint, додека распоредувањата остануваат ризични, неуспесите тешко се дијагностицираат, а секоја мала функционалност налага обиколка низ испреплетениот апликациски код. Резултатот можеби ќе има добри benchmark резултати во тивко попладне, но сепак ќе се движи бавно кога се вклучени клиенти, рокови и продукциски сообраќај.
Перформансите се важни. Но пред сè е важна предвидливата брзина на backend развојот: способноста да се разбере системот, да се направи ограничена промена, безбедно да се испорача и брзо да се закрепне кога реалноста не се согласува со планот. Тоа е архитектурата за која вреди да се оптимизира.
Брзината е својство на системот, а не бројка на контролна табла
Графикот на времето на одговор е корисен, но нецелосен. Услугата може да биде брза, а сепак оперативно бавна ако промената на шемата бара прекин, ако повторните обиди создаваат дупликат записи или ако неуспех во редицата остава задачи во непозната состојба.
Backend перформансите имаат неколку димензии:
- Латентност на барањата и пропусност при нормално оптоварување.
- Однесување при неуспех кога зависностите се бавни или недостапни.
- Безбедност на распоредувањето и јасност при враќање назад.
- Еволуција на базата на податоци без кревка координација.
- Доверба на развивачите при менување на постојното однесување.
Кога овие димензии се предвидливи, и работата на перформансите станува полесна. Можете да измерите тесно грло, да промените една променлива, да распоредите и да му верувате на резултатот. Без таа основа, оптимизацијата најчесто е нагаѓање обвиткано во итност.
Започнете со граници што ја прават промената локална
Во PHP апликација, примамливо е контролерите да валидираат влез, да носат деловни одлуки, да повикуваат ORM, да испраќаат известувања и да обликуваат JSON одговори. Функционира сè додека секое ново барање не ги допре сите пет области.
Поиздржлива форма е едноставна: HTTP-грижите нека останат на работ, деловните одлуки нека бидат во апликациските услуги, а инфраструктурните детали нека се изолираат зад фокусирани интерфејси. Ова не бара големо препишување на framework. Бара одолевање на поривот следниот контролер „само да се справи со тоа“.
final class CreateOrder
{
public function __construct(
private OrderRepository $orders,
private PaymentGateway $payments,
) {
}
public function handle(CreateOrderRequest $request): Order
{
$order = Order::fromRequest($request);
$this->payments->authorize($order->total(), $request->paymentToken());
$this->orders->save($order);
return $order;
}
}
Вредноста не е апстракција заради самата себе. Случајот на употреба може да се тестира без HTTP-поставување, давателот на плаќања може да се замени без препишување на правилата за нарачки, а контролерот има јасна задача: да го преведе барањето во апликациската граница.
Добрите граници помагаат и при истражување на перформансите. Ако endpoint е бавен, можете да утврдите дали времето се троши во парсирање на барањето, доменска работа, пристап до база на податоци или надворешен повик. Единствен метод што прави сè го крие тој одговор.
Дизајнирајте API-и за повторливи исходи
API-ите се договори и со клиентите и со идните одржувачи. Предвидливоста почнува со експлицитни влезови, стабилни облици на грешки и идемпотентно однесување таму каде што клиентите може повторно да се обидат.
Разгледајте POST барање поврзано со плаќање. Мрежите откажуваат откако серверот ја прифатил работата, но пред клиентот да добие одговор. Повторниот обид на клиентот е разумен. Ако backend секој повторен обид го третира како нова нарачка, системот создал проблем со исправноста, а не само API проблем.
Клучот за идемпотентност му дава на серверот начин да ја препознае истата наменета операција. Чувајте го клучот со резултирачката операција или состојбата на одговорот, наметнете единственост на ниво на база на податоци и вратете го првичниот исход за повторен клуч. Деталите се разликуваат по домен, но принципот е стабилен: повторните обиди не смеат тивко да ги умножуваат споредните ефекти.
Слично, разликувајте неуспеси на валидацијата од привремени неуспеси на зависностите. Неисправно обликувано барање треба да добие јасна грешка од клиентска страна. Времето на чекање при комуникација со друга услуга треба да биде ограничено, евидентирано и обработено според безбедносните барања на операцијата. Повторувањето на неповратно дејство без стратегија за идемпотентност може да биде полошо од брзо откажување.
Нека базата на податоци ги наметнува вистините што ѝ припаѓаат
Валидацијата во апликацијата ја подобрува повратната информација за корисникот, но не е замена за ограничувањата во базата на податоци. Две истовремени барања можат да ја поминат проверката на ниво на апликација „дали ова постои?“ пред кое било од нив да запише.
Користете ја базата на податоци за да ги заштитите инваријантите: единствени индекси за идентификатори што мора да бидат единствени, надворешни клучеви кога односите ги бараат, ограничувања за не-null за задолжителни податоци и трансакции за промени што мора да успеат заедно. Овие избори ги претвораат претпоставките во применливи правила.
Индексите ја заслужуваат истата дисциплина. Додавајте ги затоа што им се потребни на измерените обрасци на барања, а не затоа што некоја колона звучи важна. Индексот може да ги подобри читањата, додека додава трошок при запишување и дополнително зафаќање простор. Испитајте го вистинското барање, неговите филтри, редоследот на сортирање и очекуваната кардиналност пред индексот да го третирате како универзален лек.
Направете ги промените на шемата проширливи и повратни
Безбедна секвенца за миграција често го одвојува проширувањето од чистењето. Прво додадете nullable колона или нова табела. Распоредете код што може да ги чита и старите и новите претставувања. Ако е потребно, пополнете ги податоците наназад во контролирани серии. Префрлете ги запишувањата, потврдете го однесувањето, па отстранете ги старите патеки во подоцнежно издание.
Ова е помалку драматично од една миграција што преименува колона и ги ажурира сите повикувачи одеднаш. Исто така е многу попријателски за постепени распоредувања, одложени workers и одлуки за враќање назад.
Користете Docker за да ги намалите изненадувањата од околината
Контејнерите не создаваат автоматски доверливи системи, но можат да ги направат локалниот развој и однесувањето при распоредување поконзистентни. Корисната цел е усогласеност на зависностите и очекувањата за стартување, а не стек од контејнери што го имитира секој продукциски детаљ.
За PHP услуга, направете ја конфигурацијата за извршување експлицитна преку променливи на околината, одржувајте ја сликата фокусирана на вистинските потреби на апликацијата и избегнувајте вградување тајни во сликите. Дефинирајте проверки на здравјето околу значајна подготвеност: процес што започнал не е нужно подготвен да опслужува сообраќај.
Оперативните команди нека бидат едноставни и документирани. Ако worker мора да работи одделно од веб-барањата, направете го тоа експлицитен процес со сопствен надзор и логови. Ако апликацијата бара миграции, намерно одлучете дали се извршуваат во контролиран чекор на распоредување или преку друг управуван механизам. Не оставајте критични промени на состојбата на кој било контејнер што случајно ќе стартува прв.
Оптимизирајте дури откако ќе можете да ја набљудувате патеката
Штом системот е разбирлив, работата на перформансите станува практична. Мерете го траењето на барањата, бројот и траењето на барањата кон базата на податоци, времето на надворешните повици, староста на редицата, стапката на грешки и заситеноста на ресурсите. Каде што е можно, поврзете ги со идентификатор на барање или задача.
Потоа оптимизирајте го ограничувачкиот фактор. Отстранете случајно N+1 барање. Кеширајте навистина стабилен, скап резултат со јасни правила за поништување. Преместете несуштинска работа во редица. Пагинирајте големи колекции. Поставете временски ограничувања за излезните барања. Секое од овие е вредно во соодветен контекст; ниту едно не е замена за знаењето што прави системот.
Бидете особено внимателни со кеширањето. Тоа ја заменува пресметката со сложеност на поништување, користење меморија и однесување со застарени податоци. Кешот е дизајнерска одлука со начин на откажување, а не декоративен слој за брзина.
Изберете едноставни одлуки што ги зачувуваат опциите
Предвидливата брзина доаѓа од намалување на бројот на изненадувања што може да ги внесе една промена. Претпочитајте експлицитни договори наместо скриени конвенции, мали промени што може да се распоредат наместо сеопфатни препишувања и ограничувања наместо племенско знаење. Третирајте ги повторните обиди, временските ограничувања, миграциите и набљудливоста како основно однесување на производот, а не како работа за чистење.
Backend што се памети не е оној со најпаметната оптимизација. Тој е оној што му овозможува на способен тим брзо да одговори на три прашања: што се промени, на што ќе влијае и како да закрепнеме ако не успее? Градете за тие одговори, и значајните добивки во перформансите ќе имаат безбедно место каде да се применат.