ИТ развој

System Architecture: Planning for Failure, Not Just Success

Системска архитектура: Планирање за неуспех, не само за успех

Повеќето архитектонски дијаграми раскажуваат утешна приказна. Пристигнува барање, апликацијата го обработува, базата на податоци одговара и корисен резултат се враќа до корисникот. Тој успешен тек е важен, но ретко таму системите ја стекнуваат својата репутација.

Продукциските системи живеат во помалку елегантните моменти: давател на услуги за плаќање одговара премногу бавно, базенот со конекции до базата на податоци е исцрпен, Docker контејнер се рестартира на половина од задача или клиентот повторува барање откако серверот веќе го завршил. Добрата архитектура не претпоставува дека овие настани се исклучителни. Таа ги прави разбирливи, ограничени и поправливи.

Почнете од начините на кои барањето може да не успее

За секоја важна крајна точка, поставете намерно песимистичко прашање: што се случува ако овој чекор не успее откако претходниот чекор успеал?

Разгледајте API што создава нарачка. Тој може да го валидира внесот, да резервира залиха, да запише ред за нарачка, да наплати преку давател на услуги за плаќање и да објави настан за е-пошта или исполнување. Третирањето на ова како една непрекината операција повикува на неконзистентна состојба. Трансакција на база на податоци може да ја заштити работата во базата, но не може безбедно да повлече надворешен повик за плаќање или веќе испратена е-пошта.

Практичниот одговор е состојбата да се направи експлицитна. Зачувајте нарачка со јасен статус како pending, confirmed или payment_failed. Запишете доволно информации за да го продолжите или усогласите работниот тек. Потоа направете ја секоја транзиција намерна, наместо да се надевате дека долго барање ќе заврши совршено.

На повторните обиди им требаат граници, а не оптимизам

Повторните обиди се корисни кога неуспехот е привремен: мрежен истек на време, краткотрајно преоптоварување или рестартирање на услуга. Тие се штетни кога една бавна зависност ја претвораат во поплава од дуплирана работа.

Политиката за повторни обиди треба да одговори на четири прашања:

  • Кои неуспеси веројатно се привремени?
  • Колку обиди се прифатливи пред работата да се прикаже за преглед?
  • Колку долго треба да чека секој обид?
  • Дали повторувањето на операцијата може да создаде дуплиран ефект?

Користете ограничени повторни обиди со растечки одложувања и имајте го предвид рокот на барањето. Повторувањето на зависност трипати не е корисно ако секој обид може да го потроши целиот истек на време на веб-барањето. Работниците во заднина често се подобро место за бавна работа што може повторно да се обиде отколку синхроно HTTP барање.

Идемпотентноста е придружник на повторните обиди. Ако клиентот го испрати истото барање за нарачка двапати затоа што не го примил првиот одговор, системот треба да создаде една нарачка, а не две. Клуч за идемпотентност генериран од клиентот може да се зачува со операцијата и да се заштити со единствено ограничување во базата на податоци. Апликацијата потоа може да го врати оригиналниот резултат кога повторно ќе се види истиот клуч.

$order = DB::transaction(function () use ($request) {
    return Order::firstOrCreate(
        ['idempotency_key' => $request->header('Idempotency-Key')],
        ['status' => 'pending', 'total_cents' => $request->integer('total_cents')]
    );
});

Точниот API на рамката ќе се разликува, но архитектонската поента не: исправноста мора да преживее повторена испорака.

Одржувајте ја вистината во базата на податоци и надворешните ефекти усогласени

Еден чест образец на неуспех е запишување запис во база на податоци, а потоа објавување порака. Ако процесот се сруши меѓу тие чекори, базата на податоци вели дека промената се случила, но последователните системи никогаш не слушаат за неа.

Образецот outbox го решава ова без да се преправа дека распределените трансакции се едноставни. Во истата трансакција на базата на податоци што го менува деловниот запис, запишете outbox ред што го опишува настанот. Одделен работник чита необјавени outbox редови, ги испраќа до посредникот или надворешната услуга и ги означува како испорачани само по успешно предавање.

Ова го менува проблемот од „можат ли два системи да извршат commit во точно истиот миг?“ во „можеме ли сигурно повторно да се обидеме со траен запис?“ Вториот проблем сè уште е реален, но е решлив. Потрошувачите исто така мора да толерираат дупликати настани, бидејќи работникот може успешно да објави и да се сруши пред да го запише тој успех.

Осмислете однесување при преоптоварување пред сообраќајот да го наметне тоа

Перформансите не се само во тоа успешните барања да бидат брзи. Тие се и во избегнување колапс кога зависноста забавува. Преоптоварена PHP апликација може да остане без капацитет на работници додека чека повици кон зависни системи; тогаш дури и лесните здравствени проверки и дејствата за опоравување стануваат тешки.

Намерно поставете истек на време за конекции до база на податоци, HTTP клиенти, редици и повици до кешот. Недостасувачкиот истек на време често е неограничено чекање маскирано како стандардна вредност. Усогласете ги базените со конекции и бројот на PHP работници со капацитетот на базата на податоци и зависните услуги. Повеќе паралелност во апликацијата не значи автоматски и поголема пропусност.

Редиците се вредни вентили за притисок, но само кога имаат ограничувања и оперативна видливост. Дефинирајте што треба да се случи кога редицата расте: одбијте несуштинска работа, одложете ја, дајте ѝ приоритет или известете оператор. Редица што прифаќа работа засекогаш само го преместува прекинот од API-то во невидливо заостанување.

Направете ја деградацијата одлука за производот

Не секоја зависност заслужува еднаков третман. Ако препораките не се достапни, страницата на производот сè уште може да биде корисна без нив. Ако не може да се проверат податоците за авторизација, продолжувањето може да биде небезбедно. Запишете ги овие разлики.

Корисните деградирани режими се тесни и искрени: прикажете кеширани податоци со соодветна политика за свежина, одложете извештај или прифатете барање и обработете го асинхроно. Избегнувајте тивко враќање нецелосни податоци како да се целосни. Корисничкото искуство треба да ја одразува вистинската сигурност на системот.

Контејнерите го подобруваат пакувањето, но не и сигурноста сами по себе

Docker ги прави средините за распоредување поконзистентни, но рестартирање на контејнер не е стратегија за опоравување на незавршена работа. Контејнерите може да бидат прекинати, повторно распоредени или стартувани покрај привремено недостапна база на податоци. Затоа апликациите треба предвидливо да се стартуваат, да изложуваат значајна крајна точка за здравствена проверка и да се справуваат со неуспесите на зависностите без да ја нарушат состојбата.

Во размислувањето, одделете ја живоста од подготвеноста. Процесот може да биде жив, но да не е во состојба да опслужува сообраќај бидејќи миграциите се нецелосни, конфигурацијата е невалидна или потребна зависност не е достапна. Автоматизацијата за распоредување треба да насочува сообраќај само кон инстанци што се подготвени за наменетата улога.

Миграциите на базата на податоци заслужуваат исто внимание. Претпочитајте компатибилни, постепени промени: додајте колона што дозволува null, распоредете код што може да работи со двете форми, безбедно пополнете ги постојните податоци, а потоа подоцна наметнете построги ограничувања. Миграција што заклучува голема табела или отстранува колона што сè уште ја користат постари контејнери може рутинско издание да претвори во прекин.

Набљудливоста е дел од архитектурата

Кога ќе се случи неуспех, првото прашање ретко е „дали нешто не успеа?“ Туку „кое барање, зависност, распоредување или транзиција на состојба го предизвика ова?“ Структурирани дневници, идентификатори на барања, известување за грешки и мал сет метрики на ниво на услуга го прават тоа прашање одговорливо.

Евидентирајте настани со контекст што помага при дијагноза без откривање тајни или лични податоци. Следете ја стапката на грешки, латентноста, староста на редицата и неуспесите на зависностите. Алармирајте за симптомите што ги чувствуваат корисниците, а не за секој бучен внатрешен настан. Целта не е контролна табла полна со графикони; туку систем што може да се објасни под притисок.

Градете за опоравување, па вежбајте го

Архитектурата свесна за неуспеси не е песимизам. Таа е почит кон реалноста и кон луѓето што ќе го управуваат софтверот. Направете ги важните операции идемпотентни, зачувувајте ги транзициите на состојба, ограничете ги повторните обиди, изолирајте ги бавните зависности, распоредувајте компатибилни промени и осигурете неуспесите да остават зад себе корисни докази.

Незаборавниот тест е едноставен: кога успешниот тек ќе се прекине на половина, може ли системот да се опорави без нагаѓање? Ако одговорот е да, архитектурата прави повеќе од обработка на барања. Таа ги штити деловните одлуки скриени во нив.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.