ИТ развој

System Design: Debugging Your Architecture Before It Breaks

Системски дизајн: Дебагирање на вашата архитектура пред да се расипе

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

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

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

Започнете со патеката на барањето

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

Разгледајте PHP endpoint што создава нарачка. Неговата среќна патека може да изгледа едноставно: валидирај внес, наплати преку давател на плаќања, зачувај ја нарачката, намали го залихот, испрати е-пошта. Но дизајнерските прашања се поважни од редоследот:

  • Што ако давателот на плаќања ја прифати наплатата, но апликацијата истече пред да ја евидентира нарачката?
  • Што ако залихата се намали двапати бидејќи клиентот повторува?
  • Што ако испораката на е-пошта не успее откако трансакцијата ќе се потврди?
  • Што ако барање за залиха заклучи популарен ред за време на скок на сообраќајот?

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

Направете го однесувањето при неуспех дел од интерфејсот

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

Поставете експлицитни временски ограничувања за појдовните повици. Недостигот на временско ограничување може да претвори бавна надолна услуга во растечки куп блокирани PHP workers. Временското ограничување треба да биде спарено со одлука: повтори, врати грешка, користи застарен кеширан одговор или продолжи асинхроно.

$response = $httpClient->request('POST', $paymentUrl, [
    'timeout' => 3.0,
    'headers' => [
        'Idempotency-Key' => $idempotencyKey,
    ],
    'json' => $payload,
]);

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

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

Испитајте ја базата на податоци пред да додадете инфраструктура

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

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

SELECT id, status, created_at
FROM orders
WHERE customer_id = :customer_id
  AND created_at >= :from
ORDER BY created_at DESC
LIMIT 50;

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

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

Користете Docker за да го намалите отстапувањето, а не да ја скриете сложеноста

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

За PHP услуга, тоа обично значи фиксирање на компатибилен PHP base image, инсталирање само на потребните екстензии, внимателно користење кеширање на зависности и извршување на апликацијата со non-root корисник каде што е практично. Тоа не значи поставување акредитиви за базата на податоци во image-от или потпирање на запишлив директориум на апликацијата за трајна состојба.

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

Внимавајте на спрегнатост маскирана како погодност

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

Одделете ги одговорностите на корисни граници. Доменските правила треба да може да се тестираат без HTTP барања. Инфраструктурните адаптери треба да ги изолираат деталите за базата на податоци и трети страни. Позадинските задачи треба да имаат јасни payload-и и политики за повторување. Ова не бара секоја класа да се подели во микросервис. Всушност, добро структуриран модуларен монолит често е најодржливиот избор сè додека потребите за независно распоредување, сопственост или скалирање не станат реални.

Истата воздржаност важи и за асинхроната работа. Редицата е одлична за бавни, повторливи или раздвоени задачи. Таа е лоша замена за дефинирање конзистентност. Штом порака се испорача барем еднаш, потрошувачите мора да толерираат дупликати. Штом пораките можат да пристигнат доцна, системот мора да одлучи дали старата работа сè уште е валидна.

Претворете ги архитектонските сомнежи во повторливи проверки

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

  • Дефинирајте однесување при успех, истекување, повторување и дупликат-барање за важните API операции.
  • Мерете ги бавните endpoint-и и испитајте ги барањата кон базата на податоци зад нив.
  • Тестирајте неуспеси на зависности во контролирана околина.
  • Документирајте сопственост и чекори за опоравување за редици, закажани задачи и миграции на податоци.
  • Претпочитајте едноставен дизајн со познати ограничувања пред дистрибуиран дизајн со невидливи.

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

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

Mihajlo

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