Надвор од микросервисите: Скалирање на API-ја со развојните потреби на системот
Микросервисите често се претставуваат како природен следен чекор штом API-то почне да расте. Пристигнува повеќе сообраќај, тимовите се множат, распоредувањата стануваат побавни, а монолитот почнува да се чувствува како ограничување. Разделувањето може да помогне — но само кога поделбата ги одразува реалните потреби на системот.
Тешката вистина е дека „микросервиси“ сами по себе не се стратегија за скалирање. Тоа е размена: помала внатрешна поврзаност во замена за мрежни граници, дистрибуирани податоци, оперативен товар и повеќе начини на откажување. Зрелата backend архитектура се развива со намерно правење на тие размени, а не со третирање на бројот на сервиси како знак на напредок.
Скалирајте ја точката на притисок, не дијаграмот
API-то може да има потешкотии поради многу причини: скапи барања кон базата на податоци, бавна зависност од трета страна, долготраен извоз, проблематичен закупец, тесно грло при распоредување или преоптоварен тим. Не сите овие проблеми бараат нов сервис.
Почнете со утврдување што навистина е под притисок. Ако крајната точка за каталог на производи е бавна затоа што спојува големи табели и применува неколку филтри, извлекувањето на „catalog-service“ нема да го направи барањето поевтино. Може да додаде HTTP повик кон истото скапо барање. Подобри први чекори може да вклучуваат преглед на индекси, пагинација, потесен облик на одговор, кеширање или модел за читање дизајниран за тој образец на пристап.
Слично, ако генерирањето PDF ги држи PHP работниците отворени со минути, корисната граница може да биде асинхрона работа наместо посебен јавен сервис. Ставете го барањето во редица, вратете идентификатор на задача и дозволете работник да ја генерира датотеката надвор од животниот циклус на барањето. Системот еволуирал, но сè уште не ги презел трошоците на дистрибуиран домен на производот.
Задржете го модуларниот монолит подолго отколку што е модерно
Добро структуриран монолит не е архитектонски неуспех. Често е најбрзиот начин да се зачува трансакциската конзистентност, да се разбере кодната база и да се испорачуваат промени додека доменот сè уште се обликува.
„Модуларен“ е клучниот збор. PHP апликацијата може да ги задржи наплатата, идентитетот, нарачувањето и известувањата во истото распоредување, додека спроведува јасна сопственост во кодот. Избегнувајте секој контролер да пристапува до секој модел. Дајте му на секој модул граница на апликацијата, експлицитни интерфејси и ограничен сет на одговорности за перзистентност.
На пример, модулот за нарачки треба да побара цена од модулот за цени преку дефиниран апликациски сервис или договор. Не треба лежерно да ги пресоздава правилата за цени во SQL, контролер и команда во заднина. Таа дисциплина овозможува идно извлекување, но исто така го прави монолитот полесен за одржување денес.
Корисни сигнали дека границата станува реална
- Потсистемот има значително различен профил на скалирање, како обработка на слики или индексирање за пребарување.
- На тимот му треба да објавува и управува со способност според независно темпо.
- Доменот има стабилна сопственост и јазик што се разбира низ целата организација.
- Потсистемот може да толерира асинхрона комуникација или има јасен договор за синхрони повици.
- Неговите податоци можат независно да се поседуваат без постојани спојувања и трансакции преку граници.
Ниту еден од овие сигнали не значи дека извлекувањето е задолжително. Заедно, тие ја прават одлуката многу полесна за оправдување.
Границите на базата на податоци обично се вистинската граница
Лесно е да се стави HTTP API пред модул и да се нарече микросервис. Тешкото прашање е дали тој ги поседува своите податоци. Ако повеќе сервиси директно читаат и запишуваат во истите табели, архитектурата има дистрибуирано распоредување без дистрибуирана сопственост.
Заедничките табели создаваат суптилна поврзаност. Миграција наменета за еден сервис може да скрши друг. Барање што изгледа безопасно може да зависи од недокументирана колона. Тимовите не можат слободно да ги менуваат шемите бидејќи секој потрошувач на базата на податоци сега е дел од планот за објавување.
Кога извлекувате способност, одлучете кој сервис е авторитативен за секој дел од податоците. Другите сервиси треба да ги добиваат потребните информации преку API-ја, настани или наменски реплицирани прегледи. Тоа воведува евентуална конзистентност, па таа мора да биде дизајнирана во корисничкото искуство и деловниот процес.
Разгледајте го инвентарот. API за нарачки може да поднесе барање за резервација, додека инвентарот останува авторитативен за достапните залихи. Сервисот за нарачки не треба самостојно да ги намалува редовите за инвентар. Ако резервацијата е асинхрона, нарачката може да влезе во состојба pending сè додека не добие потврда или неуспех. Ова е поексплицитно отколку да се преправа дека дистрибуираната операција сè уште е една трансакција во база на податоци.
Дизајнирајте за неуспех пред да додадете повици кон сервиси
Повиците на функции во процесот откажуваат предвидливо. Мрежните повици не. Тие може да истечат, да вратат доцна, да успеат откако повикувачот се откажал или да откажат додека оддалечениот систем сè уште го обработува барањето. Секоја нова синхрона зависност ги менува карактеристиките на доверливост на крајната точка.
За секој повик од сервис до сервис, дефинирајте ги практичните одговори на неколку прашања:
- Кој е временскиот рок и дали е пократок од преостанатиот буџет за барање на повикувачот?
- Дали операцијата може безбедно да се повтори и како се спречува дуплирана работа?
- Што прави повикувачот кога зависноста не е достапна?
- Кој одговор се кешира, става во редица, деградира или одбива?
- Како операторите ќе корелираат едно клиентско барање низ сервисите?
Идемпотентноста е особено важна за командите. Барањето за плаќање или создавање нарачка треба да носи клуч за идемпотентност, така што повторен обид нема да создаде второ наплаќање или дуплирана нарачка. Повторните обиди без идемпотентност не се отпорност; тие се механизам за умножување на неуспесите.
И настаните бараат внимание. Објавувајте ги сигурно, зачувајте доволно контекст за потрошувачите и направете ги потрошувачите идемпотентни. Обработувач на настан може да се изврши повеќе од еднаш. Третирањето на дуплираната испорака како исклучителна невозможност е начинот на кој започнува отстапувањето на податоците.
Контејнерите го подобруваат пакувањето, не архитектурата
Docker е вреден бидејќи го стандардизира пакувањето на извршното опкружување. PHP апликација, нејзините екстензии и нејзината конфигурација на процеси можат да се движат низ развој, тестирање и распоредување во повторлива форма. Тоа е корисно без разлика дали системот има една апликација што може да се распореди или дваесет.
Но контејнерите не ја отстрануваат потребата од набљудливост, управување со конфигурација, ракување со тајни, миграции на базата на податоци или планови за враќање назад. Флота од мали контејнери со нејасна сопственост е потешка за управување од внимателно спакуван монолит.
Одржувајте ги оперативните грижи конкретни. Проверките на здравје треба да одразуваат дали инстанцата може да го опслужи наменетиот сообраќај. Дневниците треба да бидат доволно структурирани за да ги поврзат грешките со идентификаторите на барањата. Метриките треба да разликуваат латентност, стапка на грешки, заситеност и длабочина на редицата. Миграциите на базата на податоци треба да бидат компатибилни наназад кога старите и новите верзии на апликацијата може да работат во исто време.
final class CreateOrderHandler
{
public function __invoke(CreateOrder $command): OrderId
{
$existing = $this->orders->findByIdempotencyKey($command->idempotencyKey());
if ($existing !== null) {
return $existing->id();
}
$order = Order::pending($command);
$this->orders->save($order);
$this->outbox->record(new OrderCreated($order->id()));
return $order->id();
}
}
Важниот детал не е точниот распоред на класите. Тоа е границата: зачувајте ја деловната промена и записот за настанот заедно, а потоа објавете од сигурен процес во заднина. Ова избегнува тврдење дека настан е испратен кога трансакцијата во базата на податоци подоцна ќе не успее.
Нека архитектурата остане обратлива
Најдобриот пат на еволуција обично е постепен. Прво подобрете ги границите на модулите. Потоа изолирајте ја скапата работа зад редици. Воведете наменски модел за читање каде што сообраќајот за читање го бара тоа. Извлечете една способност кога нејзината сопственост, податоци и оперативни барања се јасни. Измерете го резултатот пред да го повторите образецот.
Микросервисите може да бидат одлична цел за делови од системот. Тие се слаб стандарден идентитет за целиот систем. Трајната цел е поедноставна: секоја промена да биде разбирлива, секоја зависност намерна и секоја одлука за скалирање пропорционална на притисокот што го решава.
Така API-јата растат без да станат лавиринт од мрежни повици. Не со бркање модерна архитектура, туку со градење граници што го заслужуваат правото да постојат.