Престанете да нагаѓате за перформансите на API: Архитектирајте за предвидливост
Повеќето проблеми со перформансите на API започнуваат со приказна што звучи разумно: крајната точка веројатно е бавна затоа што базата на податоци е зафатена, на контејнерот му треба повеќе меморија или сообраќајот сигурно нагло пораснал. Понекогаш таа приказна е точна. Почесто, тоа е претпоставка направена пред некој да дефинира што значи „брзо“ или каде навистина се троши времето.
Предвидливите перформанси се подобра инженерска цел од повремената брзина. Одговор што доследно се завршува во рамките на разбраниот буџет е полесен за управување, скалирање, тестирање и објаснување отколку оној што обично е брз, но повремено се распаѓа при вообичаено оптоварување. Работата започнува во архитектурата, многу пред контролната табла да стане црвена.
Претворете го „брзо“ во буџет за време на одговор
Барањето до API е низа од задачи: рутирање, автентикација, валидација, апликациска логика, пристап до база на податоци, серијализација и мрежна испорака. Ако крајната точка има цел за латентност, секоја значајна фаза треба да добие дел од таа цел.
На пример, крајна точка со цел од 300 ms за услугата може да резервира време за автентикација и валидација, да дозволи ограничено време за работа со базата на податоци и да остави резерва за серијализација и нормални варијации во инфраструктурата. Точните вредности зависат од системот, но самото буџетирање ги менува разговорите за дизајнот. „Можеме ли да додадеме уште едно барање?“ станува „Дали ова се вклопува во делот од буџетот за барањето наменет за базата на податоци?“
Користете перцентили наместо просеци при оценување на резултатот. Просекот може да изгледа здраво додека мал дел од барањата се болно бавни. Најбавниот практичен дел од вообичаените барања обично е местото каде што стануваат видливи конкуренцијата за заклучувања, промашувањата во кешот, исцрпените пулови за конекции и преголемите товари.
Направете ги скапите патеки експлицитни
Перформансите стануваат непредвидливи кога трошокот е скриен зад практични апстракции. ORM релација до која се пристапува во циклус, серијализатор што мрзеливо презема поврзани податоци или помошна функција што повикува надворешна услуга може да претвори едноставна крајна точка во неограничена количина работа.
Разгледајте PHP крајна точка што враќа нарачки и имиња на нивните купувачи. Верзијата што изгледа јасно може да изврши едно барање за нарачките, а потоа едно барање за купувач за секоја нарачка:
$orders = $orderRepository->recent();
foreach ($orders as $order) {
$result[] = [
'id' => $order->id(),
'customer' => $order->customer()->name(),
];
}
Ако customer() ги вчитува податоците мрзеливо, трошокот расте со бројот на нарачки. Подобрувањето не е само „оптимизирајте го барањето“. Однапред дефинирајте ја формата на податоците: преземете ги потребните нарачки и полиња за купувачите со ограничено барање или намерно групно преземање, а потоа серијализирајте ги веќе вчитаните податоци. Крајната точка треба да извршува приближно иста количина работа за споредливи големини на барања.
Пагинацијата бара иста дисциплина. Ограничување без стабилно подредување не е дизајн за перформанси. Пагинацијата заснована на поместување може да станува сè поскапа кај големи, често променливи множества податоци. За текови подредени според уникатна, индексирана вредност, пагинацијата со курсор често обезбедува постабилна патека бидејќи базата на податоци може да продолжи од позната позиција наместо постојано да прескокнува претходни редови.
Дизајнирајте ги барањата до базата на податоци според шаблоните на пристап
Индексот е корисен кога го поддржува вистинскиот шаблон на филтрирање, спојување и подредување. Реактивното додавање индекси на поединечни колони може да го зголеми трошокот при запишување без да го подобри важното барање. Почнете од обликот на барањето на крајната точка, проверете го неговиот план за извршување и потврдете дека избраниот индекс ги стеснува и подредува податоците на корисен начин.
Одржувајте ги трансакциите кратки. Трансакција што извршува далечински повици, обработка на датотеки или долготрајна апликациска работа додека држи заклучувања во базата на податоци ја прави латентноста зависна од неповрзани системи. Прво валидирајте ги влезовите, направете го најмалото неопходно трансакциско ажурирање и преместете ја несуштинската дополнителна работа надвор од трансакцијата.
Одделете ја работата на барањата од работата во позадина
Дел од работата припаѓа во барањето затоа што на повикувачот му е потребен нејзиниот резултат пред да продолжи. Другата работа треба само сигурно да се случи откако промената на состојбата е прифатена: испраќање е-пошта, генерирање извози, освежување податоци за пребарување или известување друг систем. Третирањето на двете категории на ист начин го прави API заложник на најбавната зависност.
Сигурен шаблон е да се потврди примарната промена на состојбата и да се запише настан или задача како дел од истата трансакција со базата на податоци. Потоа работник може да ја обработи задачата со повторни обиди и набљудливост. Ова го избегнува и спротивниот начин на откажување: успешно ажурирање на базата на податоци, но губење на известувањето бидејќи процесот откажал веднаш потоа.
Обработката во позадина не ја отстранува сложеноста; ја преместува на место каде што може да се управува со неа. На работниците им се потребни идемпотентни обработувачи, ограничени политики за повторни обиди и дефиниран исход по повторен неуспех. Повторен обид без идемпотентност може двапати да наплати картичка, да испрати дупликат пораки или постојано да ја примени истата корекција на залихите.
- Користете траен клуч за идемпотентност за операции што може да се повторат.
- Повторувајте ги минливите неуспеси со ограничувања и растечки доцнења.
- Зачувајте доволно контекст за да ги истражите неуспешните задачи без слепо повторно да ги извршувате.
- Одлучете дали трајно неуспешна задача бара рачна проверка, компензација или видлив статус за корисникот.
Поставете ограничувања на секоја граница
Предвидливоста доаѓа од одбивањето неограничена работа. Поставете максимални големини на страници, ограничувања на телото на барањето, временски ограничувања за барања, ограничувања на пуловите за конекции и јасни рокови за појдовни повици. Ова не се произволни ограничувања; тие го штитат остатокот од системот од едно скапо барање.
Појдовен HTTP повик треба да има експлицитни временски ограничувања за поврзување и за вкупното траење. Барање до база на податоци не треба да чека засекогаш за заклучување или преоптоварен сервер. Потрошувач на редица треба да контролира колку работа презема одеднаш. Без ограничувања, притисокот се шири: бавните зависности ги зафаќаат работниците, работниците држат конекции, конекциите ги ставаат барањата во редица и локален проблем станува прекин во целата апликација.
Docker го олеснува пакувањето на API, но контејнерите не ги бришат ограничувањата на ресурсите. Осигурете се дека апликацијата има намерно избран модел на процеси, доволен капацитет на работници за очекуваната конкурентност и безбеден начин да престане да прифаќа нова работа при распоредување. Контејнер што е прекинат додека обработува барање или задача треба или да заврши во рамките на прозорецот за исклучување или да ја остави работата во состојба од која може да се опорави.
Измерете ја патеката што сте ја дизајнирале
Метриките се најкорисни кога одговараат на архитектонски прашања. Следете ја латентноста на барањата по рута и класа на статус, стапките на грешки, времетраењето на барањата до базата на податоци, заситеноста на пуловите, длабочината на редицата, староста на задачите и латентноста на појдовните зависности. Додајте структурирани дневници со идентификатори на барања или корелација, за бавното барање да може да се следи низ границите на апликацијата и работниците.
Не инструментирајте сè неселективно. Инструментирајте ги границите каде што работата преминува од едни раце во други: HTTP влез, повици до базата на податоци, операции со кешот, редици и далечински услуги. Потоа тестирајте ги познатите скапи патеки со реалистични големини на товари и конкурентност. Целта не е да се создаде импресивен извештај од тест за оптоварување; туку да се дознае каде системот престанува да ги почитува своите буџети.
Предвидливоста е функција
Брзата крајна точка е задоволувачка. Предвидливата крајна точка е доверлива. Таа им овозможува на продуктните тимови да постават искрени очекувања, на операторите брзо да препознаат ненормално однесување и на развивачите да го менуваат кодот без да се потпираат на среќа.
Практичната навика е едноставна: дефинирајте буџет, ограничете ја работата, изолирајте ги бавните задачи, заштитете ја секоја зависност со ограничувања и измерете го резултатот. Кога овие избори стануваат нормална работа при дизајнирањето, перформансите престануваат да бидат игра на нагаѓање во доцна фаза и стануваат едно од најјасните ветувања на системот.