Hajutatud rakenduste ehitusplokid. Esimene lähenemine

Hajutatud rakenduste ehitusplokid. Esimene lähenemine

Viimases siit oleme analüüsinud reaktiivarhitektuuri teoreetilisi aluseid. On aeg rääkida andmevoogudest, reaktiivsete Erlangi/Elixir süsteemide juurutamise viisidest ja sõnumsidemustritest neis:

  • Taotlus-vastus
  • Päringuga tükeldatud vastus
  • Vasta taotlusega
  • avalda-telli
  • Tagurpidi Avalda Telli
  • Ülesannete jaotus

SOA, MSA ja sõnumivahetus

SOA, MSA on süsteemiarhitektuurid, mis määratlevad süsteemide ehitamise reeglid, samas kui sõnumivahetus pakub nende rakendamiseks primitiive.

Ma ei taha propageerida seda või teist süsteemiarhitektuuri. Olen konkreetse projekti ja ettevõtte jaoks kõige tõhusamate ja kasulikumate tavade rakendamise poolt. Ükskõik millise paradigma me valime, on parem luua süsteemiplokke, pidades silmas Unixi teed: minimaalse ühenduvusega komponendid, mis vastutavad üksikute üksuste eest. API-meetodid teevad olemitega kõige lihtsamaid toiminguid.

Sõnumid – nagu nimigi ütleb – sõnumite vahendaja. Selle peamine eesmärk on sõnumite vastuvõtmine ja saatmine. Ta vastutab teabe saatmise liideste, süsteemisisese teabe edastamise loogiliste kanalite moodustamise, marsruutimise ja tasakaalustamise ning tõrkekäsitluse eest süsteemi tasandil.
Arendatud sõnumside ei ürita rabbitmq-ga konkureerida ega seda asendada. Selle peamised omadused:

  • Levitamine.
    Vahetuspunkte saab luua klastri kõikidele sõlmedele, võimalikult lähedale neid kasutavale koodile.
  • Lihtsus.
    Keskenduge standardkoodi minimeerimisele ja kasutusmugavusele.
  • Parem sooritus.
    Me ei ürita korrata rabbitmq funktsionaalsust, vaid valime ainult arhitektuurse ja transpordikihi, mille sobitame OTP-sse võimalikult lihtsalt, minimeerides kulusid.
  • Paindlikkus.
    Iga teenus võib kombineerida palju vahetusmalle.
  • Disainilt vastupidavus.
  • Skaalautuvus.
    Sõnumside kasvab koos rakendusega. Koormuse kasvades saate vahetuspunktid eraldi masinatesse teisaldada.

MÄRKUS Koodikorralduse poolest sobivad metaprojektid hästi keeruliste Erlang/Elixir süsteemide jaoks. Kogu projekti kood on ühes hoidlas – katuseprojektis. Samal ajal on mikroteenused isoleeritud nii palju kui võimalik ja sooritavad lihtsaid toiminguid, mis vastutavad eraldi olemi eest. Sellise lähenemisega on lihtne hooldada kogu süsteemi API-d, lihtne on teha muudatusi, mugav on kirjutada ühiku- ja integratsiooniteste.

Süsteemi komponendid suhtlevad otse või maakleri kaudu. Sõnumite saatmise positsioonist lähtudes on igal teenusel mitu eluetappi:

  • Teenuse initsialiseerimine.
    Selles etapis toimub teenust ja sõltuvusi käivitava protsessi konfigureerimine ja käivitamine.
  • Looge vahetuspunkt.
    Teenus võib kasutada hosti konfiguratsioonis määratud staatilist vahetuspunkti või luua vahetuspunkte dünaamiliselt.
  • Teenuse registreerimine.
    Et teenus päringuid teenindaks, peab see olema vahetuspunktis registreeritud.
  • Tavaline töö.
    Teenus teeb kasulikku tööd.
  • Töö lõpetamine.
    Väljalülitamist on kahte tüüpi: tavaline ja hädaolukorras. Tavahooldusel lülitub see vahetuspunktist lahti ja peatub. Hädaolukorras käivitab sõnumside ühe tõrkesiirde stsenaariumidest.

See tundub üsna keeruline, kuid kood pole nii hirmutav. Kommentaaridega koodinäited tuuakse mallide analüüsis veidi hiljem.

Vahetused

Vahetuspunkt on sõnumsideprotsess, mis rakendab sõnumimalli komponentidega suhtlemise loogikat. Kõigis alltoodud näidetes suhtlevad komponendid vahetuspunktide kaudu, mille kombinatsioon moodustab sõnumivahetuse.

Sõnumivahetusmustrid (EP-d)

Globaalselt võib vahetusmustrid jagada kahe- ja ühepoolseteks. Esimesed tähendavad vastust sissetulevale sõnumile, teised mitte. Klassikaline näide kahesuunalisest mustrist klient-server arhitektuuris on päringu-vastuse muster. Mõelge mallile ja selle muudatustele.

Taotlus-vastus või RPC

RPC-d kasutatakse siis, kui peame saama vastuse mõnest teisest protsessist. See protsess võib toimuda samal hostil või teisel kontinendil. Allpool on diagramm kliendi ja serveri vahelisest suhtlusest sõnumivahetuse kaudu.

Hajutatud rakenduste ehitusplokid. Esimene lähenemine

Kuna sõnumside on täiesti asünkroonne, jaguneb kliendi vahetus kaheks faasiks:

  1. Päringu saatmine

    messaging:request(Exchange, ResponseMatchingTag, RequestDefinition, HandlerProcess).

    vahetus ‒ kordumatu vahetuspunkti nimi
    ResponseMatchingTag ‒ kohalik silt vastuse töötlemiseks. Näiteks mitme identse, erinevatele kasutajatele kuuluva päringu saatmise korral.
    Taotluse definitsioon ‒ taotluse keha
    HandlerProcess ‒ Käitleja PID. See protsess saab serverilt vastuse.

  2. Vastuste töötlemine

    handle_info(#'$msg'{exchange = EXCHANGE, tag = ResponseMatchingTag,message = ResponsePayload}, State)

    ResponsePayload - serveri vastus.

Serveri jaoks koosneb protsess samuti kahest etapist:

  1. Vahetuspunkti lähtestamine
  2. Sissetulevate taotluste töötlemine

Illustreerime seda malli koodiga. Oletame, et peame rakendama lihtsa teenuse, mis pakub ühte täpset ajameetodit.

Serveri kood

Teisaldame teenuse API määratluse saidile api.hrl:

%% =====================================================
%%  entities
%% =====================================================
-record(time, {
  unixtime :: non_neg_integer(),
  datetime :: binary()
}).

-record(time_error, {
  code :: non_neg_integer(),
  error :: term()
}).

%% =====================================================
%%  methods
%% =====================================================
-record(time_req, {
  opts :: term()
}).
-record(time_resp, {
  result :: #time{} | #time_error{}
}).

Määratlege teenuse kontroller failis time_controller.erl

%% В примере показан только значимый код. Вставив его в шаблон gen_server можно получить рабочий сервис.

%% инициализация gen_server
init(Args) ->
  %% подключение к точке обмена
  messaging:monitor_exchange(req_resp, ?EXCHANGE, default, self())
  {ok, #{}}.

%% обработка события потери связи с точкой обмена. Это же событие приходит, если точка обмена еще не запустилась.
handle_info(#exchange_die{exchange = ?EXCHANGE}, State) ->
  erlang:send(self(), monitor_exchange),
  {noreply, State};

%% обработка API
handle_info(#time_req{opts = _Opts}, State) ->
  messaging:response_once(Client, #time_resp{
result = #time{ unixtime = time_utils:unixtime(now()), datetime = time_utils:iso8601_fmt(now())}
  });
  {noreply, State};

%% завершение работы gen_server
terminate(_Reason, _State) ->
  messaging:demonitor_exchange(req_resp, ?EXCHANGE, default, self()),
  ok.

Kliendi kood

Teenusele päringu saatmiseks võite helistada sõnumsidepäringu API-le kõikjal kliendis:

case messaging:request(?EXCHANGE, tag, #time_req{opts = #{}}, self()) of
    ok -> ok;
    _ -> %% repeat or fail logic
end

Hajutatud süsteemis võib komponentide konfiguratsioon olla väga erinev ja päringu hetkel ei pruugi sõnumivahetus veel alata või pole teenusekontroller valmis päringut teenindama. Seetõttu peame kontrollima sõnumivastust ja käsitlema tõrkejuhtumit.
Pärast edukat saatmist kliendile saab teenus vastuse või veateate.
Käsitleme mõlemat juhtumit failis hand_info:

handle_info(#'$msg'{exchange = ?EXCHANGE, tag = tag, message = #time_resp{result = #time{unixtime = Utime}}}, State) ->
  ?debugVal(Utime),
  {noreply, State};

handle_info(#'$msg'{exchange = ?EXCHANGE, tag = tag, message = #time_resp{result = #time_error{code = ErrorCode}}}, State) ->
  ?debugVal({error, ErrorCode}),
  {noreply, State};

Päringuga tükeldatud vastus

Parem on vältida suurte sõnumite saatmist. Sellest sõltub kogu süsteemi reageerimisvõime ja stabiilne töö. Kui päringule vastamine võtab palju mälu, on poolitamine kohustuslik.

Hajutatud rakenduste ehitusplokid. Esimene lähenemine

Siin on paar näidet sellistest juhtumitest:

  • Komponendid vahetavad binaarandmeid, näiteks faile. Vastuse jagamine väikesteks osadeks aitab tõhusalt töötada mis tahes suurusega failidega ja mitte püüda mälu ületäitumist.
  • Nimekirjad. Näiteks peame valima andmebaasi tohutust tabelist kõik kirjed ja edastama need teisele komponendile.

Ma nimetan selliseid vastuseid veduriks. Igal juhul on 1024 1 MB sõnumit parem kui üks 1 GB sõnum.

Erlangi klastris saame täiendava eelise - vähendame vahetuspunkti ja võrgu koormust, kuna vastused saadetakse vahetuspunktist mööda minnes kohe adressaadile.

Vasta taotlusega

See on vestlussüsteemide ehitamiseks mõeldud RPC mustri üsna haruldane modifikatsioon.

Hajutatud rakenduste ehitusplokid. Esimene lähenemine

Avalda-tellimine (andmete levitamise puu)

Sündmuspõhised süsteemid edastavad andmed tarbijatele kohe, kui need on valmis. Seega on süsteemid kalduvamad tõukemudelile kui tõmbe- või küsitlusmudelile. See funktsioon võimaldab teil mitte raisata ressursse pidevalt andmete küsimise ja ootamise tõttu.
Joonisel on kujutatud teatud teemaga liitunud tarbijatele sõnumi edastamise protsess.

Hajutatud rakenduste ehitusplokid. Esimene lähenemine

Klassikalised näited selle mustri kasutamisest on oleku jaotus: mängumaailm arvutimängudes, turuandmed börside kohta, kasulik teave andmevoogudes.

Mõelge abonendi koodile:

init(_Args) ->
  %% подписываемся на обменник, ключ = key
  messaging:subscribe(?SUBSCRIPTION, key, tag, self()),
  {ok, #{}}.

handle_info(#exchange_die{exchange = ?SUBSCRIPTION}, State) ->
  %% если точка обмена недоступна, то пытаемся переподключиться
  messaging:subscribe(?SUBSCRIPTION, key, tag, self()),
  {noreply, State};

%% обрабатываем пришедшие сообщения
handle_info(#'$msg'{exchange = ?SUBSCRIPTION, message = Msg}, State) ->
  ?debugVal(Msg),
  {noreply, State};

%% при остановке потребителя - отключаемся от точки обмена
terminate(_Reason, _State) ->
  messaging:unsubscribe(?SUBSCRIPTION, key, tag, self()),
  ok.

Allikas saab helistada sõnumi avaldamise funktsioonile mis tahes sobivas kohas:

messaging:publish_message(Exchange, Key, Message).

vahetus - vahetuspunkti nimi,
Võti ‒ marsruutimisvõti
Sõnum - kasulik koormus

Tagurpidi Avalda Telli

Hajutatud rakenduste ehitusplokid. Esimene lähenemine

Pub-sub juurutades saate logimiseks mugava mustri. Allikate komplekt ja tarbijad võivad olla täiesti erinevad. Joonisel on kujutatud juhtum ühe tarbija ja paljude allikatega.

Ülesande jaotusmuster

Peaaegu igas projektis on edasilükatud töötlemise ülesandeid, nagu aruannete genereerimine, teatiste edastamine ja andmete vastuvõtmine kolmandate osapoolte süsteemidest. Neid ülesandeid täitva süsteemi läbilaskevõimet saab protsessorite lisamisega hõlpsasti skaleerida. Meil jääb üle vaid moodustada protsessorite klaster ja ülesanded nende vahel ühtlaselt jaotada.

Mõelge tekkivatele olukordadele 3 käitleja näitel. Isegi ülesannete jaotamise etapis kerkib küsimus jaotamise õiglusest ja käitlejate ülevoolust. Õigluse eest vastutab ringjaotus ning selleks, et vältida käitlejate ülevoolu, kehtestame piirangu. prefetch_limit. Üleminekurežiimides prefetch_limit ei võimalda ühel töötlejal kõiki ülesandeid vastu võtta.

Sõnumside haldab järjekordi ja töötlemise prioriteeti. Protsessorid saavad ülesanded saabudes. Ülesande saab edukalt lõpule viia või ebaõnnestuda:

  • messaging:ack(Tack) ‒ helistatakse sõnumi eduka töötlemise korral
  • messaging:nack(Tack) ‒ helistatakse kõikides hädaolukordades. Pärast ülesande naasmist edastab sõnum selle teisele töötlejale.

Hajutatud rakenduste ehitusplokid. Esimene lähenemine

Oletame, et kolme ülesande töötlemisel tekkis keeruline rike: töötleja 1 jooksis pärast ülesande saamist kokku, ilma et oleks olnud aega vahetuspunktile midagi teatada. Sel juhul annab vahetuspunkt töö üle teisele töötlejale pärast ack timeouti aegumist. Käitleja 3 jättis millegipärast ülesande ära ja saatis nacki, mille tulemusena läks ülesanne ka teisele käitlejale, kes selle edukalt täitis.

Esialgne kokkuvõte

Oleme purustanud hajutatud süsteemide põhilised ehitusplokid ja saanud põhiteadmised nende kasutamisest Erlangis/Elixiris.

Põhimalle kombineerides saab luua keerulisi paradigmasid, et lahendada esilekerkivaid probleeme.

Tsükli viimases osas käsitleme teenuste korraldamise, marsruutimise ja tasakaalustamise üldisi küsimusi ning räägime ka süsteemide mastaapsuse ja tõrketaluvuse praktilisest küljest.

Teise osa lõpp.

Photo Shoot Marius Christensen
Illustratsioonid saidilt websequencediagrams.com

Allikas: www.habr.com

Lisa kommentaar