Jaotatud rakenduste ehitusplokkide. Esimene lÀhenemine

Jaotatud rakenduste ehitusplokkide. Esimene lÀhenemine

Eelmisel artiklis Me kĂ€sitlesime reaktiivse arhitektuuri teoreetilisi aluseid. On aeg rÀÀkida andmevoogudest, reaktiivsete Erlang/Elixir sĂŒsteemide elluviimise viisidest ja nende sĂ”numivahetuse mustritest:

  • PĂ€ring-vastus
  • PĂ€ring-osaline vastus
  • Vastus pĂ€ringu alusel
  • Avalda-telli
  • Pöördprotsesside ĂŒlekandmine
  • Ülesannete jaotamine

SOA, MSA ja sÔnumivahetus

SOA ja MSA on sĂŒsteemiarhitektuurid, mis mÀÀravad sĂŒsteemide ĂŒlesehituse reeglid, samas kui sĂ”numivahetus pakub nende rakendamiseks primitiive.

Ma ei soovi propageerida ĂŒhte ega teist sĂŒsteemide arhitektuuri. Ma olen selle poolt, et rakendada konkreetsele projektile ja ettevĂ”ttele maksimaalselt efektiivseid ja kasulikke praktikaid. Millist paradigmat me ei valiks, on sĂŒsteemide plokkide loomine parem Unix-way jĂ€rgi: komponentide minimaalne seos, mis vastutavad eraldi entiteetide eest. API meetodid teevad entiteetidega vĂ”imalikult lihtsaid toiminguid.

SĂ”numivahetus – nagu nimest jĂ€reldub – on sĂ”numi vahendaja. Selle pĂ”hieesmĂ€rk on sĂ”numite vastuvĂ”tt ja edastamine. See vastutab teabe saatmise liideste, loogiliste teabeedastuskanalite loomise, marsruutimise ja tasakaalustamise ning sĂŒsteemitasandi rikete töötlemise eest.
Arendatav sĂ”numivahetus ei pĂŒĂŒa konkureerida rabbitmq-ga ega asendada seda. Selle peamised omadused on:

  • Jagatud olemus.
    Vahetuspunktid saab luua kÔikides klastriteosades, maksimaalselt lÀhedale koodile, mis neid kasutab.
  • Lihtsus.
    Minimaalne malli koodi ja kasutusmugavuse suund.
  • Parim jĂ”udlus.
    Me ei pĂŒĂŒa kopeerida rabbitmq funktsionaalsust, vaid eristame ainult arhitektuurilist ja transpordikihti, mille integreerimine OTP-sse on vĂ”imalikult lihtne, minimeerides kulusid.
  • Paindlikkus.
    Iga teenus vÔib hÔlmata mitmeid sÔnumivahetuse mustreid.
  • TĂ”rketaluvus, mis on sisustatud disaini.
  • Skaleeritavus.
    SÔnumivahetus kasvab koos rakendusega. Koormuse suurenedes saate vahetuspunktid eraldi masinatesse viia.

MĂ€rkus. Koodiorganisatsiooni seisukohalt sobivad keerukate sĂŒsteemide jaoks Erlangi/Elixiri metaprojektid hĂ€sti. Kogu projekti kood asub ĂŒhes hoidlas ‒ katuseprojekti all. Sellega samal ajal on mikroteenused maksimaalselt eraldatud ja teevad lihtsaid operatsioone, mis vastavad eraldi entiteedile. Sellise lĂ€henemisega on lihtne toetada kogu sĂŒsteemi API-d, hĂ”lpsasti teha muudatusi ja mugavalt kirjutada ĂŒksus- ja integreerimisteste.

SĂŒsteemi komponendid suhtlevad vahetult vĂ”i lĂ€bi maakleri. Messaging'u seisukohalt on igal teenusel mitu eluetappi:

  • Teenuse initsialiseerimine.
    Sellel etapil toimub teenuse kÀitamisprotsessi ja sÔltuvuste konfigureerimine ja kÀivitamine.
  • Vahetuspunkti loomine.
    Teenusel vĂ”ib olla staatiline vahetuspunkt, mis on mÀÀratud sĂ”lme konfiguratsioonis, vĂ”i vĂ”ib see luua vahetuspunktid dĂŒnaamiliselt.
  • Teenuse registreerimine.
    Kuna teenus peab teenindama pÀringuid, tuleb see registreerida vahetuspunktis.
  • Normaalne toimimine.
    Teenuse kasulik töö.
  • Töö lĂ”petamine.
    Töö lĂ”petamiseks on kaks varianti: regulaarne ja avarii. Regulaarse lĂ”petamise korral katkestatakse teenus vahetuspunktist ja peatub. Avarii korral tĂ€idab messaging ĂŒhe tĂ”rke töötlemise stsenaariumidest.

See tundub ĂŒsna keeruline, kuid koodis pole kĂ”ik nii hirmus. KoodinĂ€idised koos kommentaaridega esitatakse malle kĂ€sitlevas osas veidi hiljem.

Vahetused

Vahetuspunkt on messaging'u protsess, mis rakendab loogikat komponentide vahelise suhtluse jaoks vastavalt sÔnumivahetuse malli. KÔikidel allpool esitatud nÀidistel suhtlevad komponendid vahetuspunktide kaudu, mille kombinatsioon moodustab messaging'u.

SÔnumivahetuse mustrid (MEP-d)

Globaalsete mustrite kohaselt vĂ”ib vahetused jaotada kahepoolsed ja ĂŒhekordsed. Esimene neist eeldab vastust saabunud sĂ”numile, teine aga mitte. Kliendi-serveri arhitektuuris on klassikaliseks nĂ€iteks kahepoolne mustri malli Request-response. Vaatame mustrit ja selle modifikatsioone.

Request–response vĂ”i RPC

RPC-d kasutatakse siis, kui me peame saama vastuse teisest protsessist. See protsess vÔib töötada samal sÔlmel vÔi asuda teisel kontinendil. Allpool on esitatud skeem kliendi ja serverilt vahetuspunkti kaudu.

Jaotatud rakenduste ehitusplokkide. Esimene lÀhenemine

Kuna messaging on tĂ€ielikult asĂŒnkroonne, jaguneb kliendi vahetus kaheks faasiks:

  1. PĂ€ringu saatmine

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

    Exchange ‒ unikaalne vahetuspunkti nimi
    ResponseMatchingTag ‒ kohalik silt vastuse töötlemiseks. NĂ€iteks, kui saadetakse mitu sama taotlust erinevatelt kasutajatelt.
    RequestDefinition ‒ taotluse keha
    HandlerProcess ‒ töötleja PID. See protsess saab serverilt vastuse.

  2. Vastuse töötlemine

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

    ResponsePayload ‒ serveri vastus.

Serveri protsess koosneb ka kahest faasist:

  1. Vahetuspunkti initsialiseerimine
  2. Saadetud taotluste töötlemine

Ilustreerime seda nÀidet koodiga. Oletame, et peame rakendama lihtsat teenust, mis pakkub ainukest tÀpset aja meetodit.

Serveri kood

Viime teenuse API mÀÀratlemise api.hrl faili:

%% =====================================================
%%  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ÀÀratleme teenuse kontrolleri time_controller.erl

%% NÀites on nÀidatud vaid oluline kood. Selle lisamine gen_server mallile annab töötava teenuse.

%% gen_serveri initsialiseerimine
init(Args) ->
  %% ĂŒhendamine vahetuspunktiga
  messaging:monitor_exchange(req_resp, ?EXCHANGE, default, self())
  {ok, #{}}.

%% kontakt kaotamise sĂŒndmuse töötlemine vahetuspunktiga. Sama sĂŒndmus esitatakse, kui vahetuspunkt pole veel kĂ€ivitatud.
handle_info(#exchange_die{exchange = ?EXCHANGE}, State) ->
  erlang:send(self(), monitor_exchange),
  {noreply, State};

%% API töötlemine
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_serveri lÔpetamine
terminate(_Reason, _State) ->
  messaging:demonitor_exchange(req_resp, ?EXCHANGE, default, self()),
  ok.

Kliendi kood

Kliendis teenusele pÀringu saatmiseks saab igal pool kutsuda messaging request API-d:

case messaging:request(?EXCHANGE, tag, #time_req{opts = #{}}, self()) of
    ok -> ok;
    _ -> %% kordamise vÔi ebaÔnnestumise loogika
end

Jaotatud sĂŒsteemis vĂ”ivad komponentide konfiguratsioonid olla erinevad ning pĂ€ringu ajal ei pruugi messaging kĂ€ivituda vĂ”i teenuse kontroller ei pruugi pĂ€ringut teenindamiseks valmis olla. SeetĂ”ttu peame kontrollima messagingu vastust ja töötlema ebaĂ”nnestumise juhtumi.
PÀrast eduka saatmise korral saab klient teenuselt vastuse vÔi vea.
KÀsitleme mÔlemat juhtumit handle_info-s:

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Ă€ring-osaline vastus

Suurem sĂ”numite saatmine ei ole soovitatav. See mĂ”jutab kogu sĂŒsteemi reageerimist ja stabiilsust. Kui vastus pĂ€ringule vĂ”tab palju mĂ€lu, on jagamine osadeks hĂ€davajalik.

Jaotatud rakenduste ehitusplokkide. Esimene lÀhenemine

Tooksin paar sellist juhtumit:

  • Komponendid vahetavad binaarandmeid, nĂ€iteks faile. Vastuse jagamine vĂ€ikesteks osadeks aitab tĂ”husalt töötada mis tahes suurusega failidega ning vĂ€ltida mĂ€lu ĂŒlevoolu.
  • Loendid. NĂ€iteks peame valima kĂ”ik kirjed tohutust andmebaasi tabelist ja saatma need teisele komponendile.

Ma kutsun selliseid vastuseid veduriteks. Igatahes on 1024 sĂ”numit, igaĂŒhes 1 MB, parem kui ĂŒksainus sĂ”num, mille suurus on 1 GB.

Erlangi klastris saame tĂ€iendava eelise – koormuse vĂ€hendamine vahetuspunktile ja vĂ”rgu koormusele, kuna vastused suunatakse otse saajale, mööda vahetuspunkti.

Vastus pÀringu alusel

See on ĂŒsna haruldane RPC-mudeli modifikatsioon dialoogisĂŒsteemide loomisel.

Jaotatud rakenduste ehitusplokkide. Esimene lÀhenemine

Publish-subscribe (andmete jaotamise puu)

SĂŒndmustepĂ”hised sĂŒsteemid toimetavad andmed tarbijatele kohe, kui need on valmis. Nii kalduvad sĂŒsteemid pigem push-mudelile kui pull vĂ”i poll. See omadus vĂ”imaldab mitte raisata ressursse, pidevalt kĂŒsides ja oodates andmeid.
Joonisel on kujutatud sÔnumi edastamise protsessi tarbijatele, kes on registreeritud konkreetse teema jaoks.

Jaotatud rakenduste ehitusplokkide. Esimene lÀhenemine

Klassikalised nÀited selle mustri kasutamisest on oleku levitamine: mÀngumaailma arvutimÀngudes, turuandmed börsil, kasulik informatsioon andmevoogudes.

Vaatame tellija koodi:

init(_Args) ->
  %% registreerume vahetuspunktis, vÔti = key
  messaging:subscribe(?SUBSCRIPTION, key, tag, self()),
  {ok, #{}}.

handle_info(#exchange_die{exchange = ?SUBSCRIPTION}, State) ->
  %% kui vahetuspunkt pole saadaval, proovime uuesti ĂŒhenduda
  messaging:subscribe(?SUBSCRIPTION, key, tag, self()),
  {noreply, State};

%% töötleme saabunud sÔnumeid
handle_info(#'$msg'{exchange = ?SUBSCRIPTION, message = Msg}, State) ->
  ?debugVal(Msg),
  {noreply, State};

%% tarbija peatamisel katkestame ĂŒhenduse vahetuspunktiga
terminate(_Reason, _State) ->
  messaging:unsubscribe(?SUBSCRIPTION, key, tag, self()),
  ok.

Allikas vÔib kutsuda esile sÔnumi avaldamise mis tahes soovitud kohas:

messaging:publish_message(Exchange, Key, Message).

Exchange ‒ vahetuspunkti nimi,
Ava ‒ marsruudi vĂ”ti,
SĂ”num, ‒ koormus,

Pöördprotsesside ĂŒlekandmine

Jaotatud rakenduste ehitusplokkide. Esimene lÀhenemine

Pub-sub‘i juurutamine vĂ”imaldab saada logimisele mugava mustri. Allikate ja tarbimise komplekt vĂ”ib olla tĂ€iesti erinev. Joonisel on kujutatud olukorda, kus on ĂŒks tarbija ja palju allikaid.

Ülesannete jaotamise muster.

Pealmost igas projektis tekivad viivitusega töötlemise ĂŒlesanded, nagu aruannete koostamine, teavituste saatmine, andmete saamine vĂ€liste sĂŒsteemide kaudu. Nende ĂŒlesannete tĂ€itmise sĂŒsteemi lĂ€bilaskevĂ”imet saab hĂ”lpsasti skaleerida, lisades töötlejat. Meie ĂŒlesanne on moodustada töötlejate klaster ja jaotada ĂŒlesanded nende vahel vĂ”rdselt.

Vaatleme tekkinud olukordi kolme töötleja nĂ€itel. Juba ĂŒlesannete jagamise etapis tĂ”useb esile Ă”igusemĂ”istmise kĂŒsimus ja töötlejate ĂŒlevoolu vĂ€ltimine. ÕigusemĂ”istmise eest vastutab round-robin jaotamine ning et vĂ€ltida töötlejate ĂŒlevoolu, seame piirangu prefetch_limit. Üleminekuperioodides prefetch_limit ei luba ĂŒhelgi töötlejal saada kĂ”iki ĂŒlesandeid.

Messsage haldab jĂ€rjekordi ja töötlemise prioriteete. Töötlejad saavad ĂŒlesandeid nende saabumise jĂ€rjekorras. Ülesande tĂ€itmine vĂ”ib lĂ”ppeda kas edukalt vĂ”i ebaĂ”nnestumisega:

  • messaging:ack(Tack) ‒ kutsutakse esile sĂ”numi eduka töötlemise korral.
  • messaging:nack(Tack) ‒ kutsutakse esile igasugustes erakorralistes olukordades. PĂ€rast ĂŒlesande tagastamist edastab sĂ”numiteenus selle teisele töötlejale.

Jaotatud rakenduste ehitusplokkide. Esimene lÀhenemine

Olgu öeldud, et kolme ĂŒlesande töötlemisel toimus keeruline ebaĂ”nnestumine: töötleja 1 kukkus pĂ€rast ĂŒlesande saamist kokku, ilma et oleks jĂ”udnud vahetuspunktile midagi teatada. Sellisel juhul edastab vahetuspunkt ĂŒlesande pĂ€rast ack timeout'i möödumist teisele töötlejale. Töötleja 3 keeldus mingil pĂ”hjusel ĂŒlesandest ja saatis nack'i, lĂ”puks lĂ€ks ĂŒlesanne ka teisele töötlejale, kes selle edukalt tĂ€itis.

EelkokkuvÔte

Oleme kĂ€sitlenud jaotatud sĂŒsteemide pĂ”hikivisid ning omandanud nende rakendamise pĂ”hiteadmisi Erlangis/Elixir'is.

Kombineerides pÔhimustreid, on vÔimalik luua keerulisi paradigmadesse probleemide lahendamiseks.

Selle tsĂŒkli lĂ”pposas kĂ€sitleme teenuste korraldust, marsruutimist ja koormuse tasakaalustamist, samuti rÀÀgime sĂŒsteemide skaleeritavuse ja talitluspidevuse praktilisest kĂŒljest.

Teise osa lÔpp.

Foto Marius Christensen
Illustratsioonid on koostatud websequencediagrams.com abil.

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster