Banatutako aplikazioen eraikuntza-blokeak. Lehen hurbilketa

Banatutako aplikazioen eraikuntza-blokeak. Lehen hurbilketa

Azkenean Artikulu arkitektura erreaktiboaren oinarri teorikoak aztertu ditugu. Datu-fluxuei, Erlang/Elixir sistema erreaktiboak ezartzeko moduei eta horietan mezularitza-ereduei buruz hitz egiteko garaia da:

  • Eskaera-erantzuna
  • Eskaeraren zatikako erantzuna
  • Eskaerarekin erantzuna
  • Argitaratu-harpidetu
  • Alderantzikatua argitaratu Harpidetza
  • Zereginen banaketa

SOA, MSA eta mezularitza

SOA, MSA sistema-arkitekturak dira, sistemak eraikitzeko arauak definitzen dituztenak, eta mezularitzak horiek inplementatzeko primitiboak eskaintzen ditu.

Ez dut sistemaren arkitektura hau edo beste hedatu nahi. Proiektu eta negozio jakin baterako praktika eraginkorrenak eta erabilgarrienak aplikatzearen alde nago. Aukeratzen dugun paradigma edozein dela ere, hobe da sistema-blokeak sortzea Unix-era begira: konektagarritasun minimoa duten osagaiak, entitate indibidualen arduradunak. API metodoek ekintza errazenak egiten dituzte entitateekin.

Mezularitza - izenak dioen bezala - mezuen bitartekari bat. Bere helburu nagusia mezuak jasotzea eta bidaltzea da. Informazioa bidaltzeko interfazeez arduratzen da, sistemaren barruan informazioa transmititzeko kanal logikoen eraketaz, bideratzeaz eta orekatzeaz, baita sistema mailan akatsak kudeatzeaz ere.
Garatutako mezuak ez du saiatzen rabbitmq-rekin lehiatzen edo ordezkatzen. Bere ezaugarri nagusiak:

  • Banaketa.
    Truke-puntuak klusterreko nodo guztietan sor daitezke, haiek erabiltzen dituen kodetik ahalik eta hurbilen.
  • Sinpletasuna.
    Zentratu erregulazio-kodea murriztera eta erabiltzeko erraztasuna.
  • Errendimendu hobea.
    Ez gara rabbitmq-en funtzionaltasuna errepikatzen saiatzen ari, baizik eta arkitektura eta garraio geruza soilik hautatzen dugu, OTPn ahalik eta modurik errazenean sartzen duguna, kostuak gutxituz.
  • Malgutasuna.
    Zerbitzu bakoitzak truke-txantiloi asko konbina ditzake.
  • Erresilientzia diseinuaren arabera.
  • Eskalagarritasuna.
    Mezularitza aplikazioarekin hazten da. Karga handitzen doan heinean, truke-puntuak makina bereizietara eraman ditzakezu.

Oharra. Kodearen antolaketari dagokionez, meta-proiektuak oso egokiak dira Erlang/Elixir sistema konplexuetarako. Proiektu-kode guztia biltegi batean dago - aterki proiektu batean. Aldi berean, mikrozerbitzuak ahalik eta gehien isolatzen dira eta entitate bereizi baten arduradun diren eragiketa errazak egiten dituzte. Planteamendu honekin, erraza da sistema osoaren APIa mantentzea, erraza da aldaketak egitea, komenigarria da unitate eta integrazio probak idaztea.

Sistemaren osagaiek zuzenean edo broker baten bidez elkarreragiten dute. Mezularitzaren posiziotik, zerbitzu bakoitzak hainbat bizitza-fase ditu:

  • Zerbitzua hasieratzea.
    Fase honetan, zerbitzua eta mendekotasunak exekutatzen dituen prozesuaren konfigurazioa eta abiarazte egiten da.
  • Sortu truke-puntu bat.
    Zerbitzuak ostalariaren konfigurazioan zehaztutako truke-puntu estatiko bat erabil dezake, edo dinamikoki truke-puntuak sor ditzake.
  • Zerbitzuaren erregistroa.
    Zerbitzuak eskaerak betetzeko, truke puntuan erregistratu behar da.
  • Funtzionamendu normala.
    Zerbitzuak lan erabilgarria egiten du.
  • Lanak amaitzea.
    2 itzalaldi mota daude: ohikoa eta larrialdikoa. Ohiko zerbitzu batekin, truke-puntutik deskonektatu eta gelditzen da. Larrialdi-kasuetan, mezuak hutsegiteko agertokietako bat exekutatzen du.

Nahiko konplikatua dirudi, baina kodea ez da hain beldurgarria. Iruzkinekin kode-adibideak txantiloien azterketan emango dira pixka bat geroago.

Trukeak

Truke-puntu bat mezularitza txantiloiaren barruan osagaiekin elkarreraginaren logika ezartzen duen mezularitza-prozesu bat da. Beheko adibide guztietan, osagaiek elkartruke puntuen bidez elkarreragiten dute, eta horien konbinazioak mezularitza osatzen du.

Mezuak trukatzeko ereduak (MEP)

Mundu mailan, truke-ereduak bi aldeetan eta alde bakarrean bana daitezke. Lehenengoek sarrerako mezuari erantzuna ematen diote, bigarrenek ez. Bezero-zerbitzariaren arkitekturan bi norabideko ereduaren adibide klasiko bat Eskaera-erantzun eredua da. Kontuan hartu txantiloia eta haren aldaketak.

Eskaera-erantzuna edo RPC

RPC beste prozesu baten erantzuna jaso behar dugunean erabiltzen da. Prozesu hau ostalari berean edo kontinente ezberdin batean egon daiteke. Jarraian, mezuen bidez bezeroaren eta zerbitzariaren arteko elkarrekintzaren diagrama bat dago.

Banatutako aplikazioen eraikuntza-blokeak. Lehen hurbilketa

Mezularitza guztiz asinkronoa denez, bezeroaren trukea 2 fasetan banatzen da:

  1. Bidali eskaera

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

    Exchange β€’ truke-puntu izen bakarra
    ResponseMatchingTag β€’ Erantzuna prozesatzeko tokiko etiketa. Esaterako, erabiltzaile ezberdinei dagozkien hainbat eskaera berdin bidaltzearen kasuan.
    Eskaeraren Definizioa β€’ eskaera gorputza
    KudeatzaileaProzesua β€’ Kudeatzailearen PID. Prozesu honek zerbitzariaren erantzuna jasoko du.

  2. Erantzunaren tratamendua

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

    ErantzunPayload - zerbitzariaren erantzuna.

Zerbitzariari dagokionez, prozesuak 2 fase ditu:

  1. Truke-puntua hasieratzea
  2. Jasotako eskaerak prozesatzea

Ilustra dezagun txantiloi hau kodearekin. Demagun denbora zehatzeko metodo bakarra eskaintzen duen zerbitzu sinple bat ezarri behar dugula.

Zerbitzariaren kodea

Muga dezagun zerbitzuaren APIaren definizioa api.hrl-era:

%% =====================================================
%%  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{}
}).

Definitu zerbitzu-kontrolatzailea time_controller.erl-en

%% Π’ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ ΠΏΠΎΠΊΠ°Π·Π°Π½ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π·Π½Π°Ρ‡ΠΈΠΌΡ‹ΠΉ ΠΊΠΎΠ΄. Вставив Π΅Π³ΠΎ Π² шаблон 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.

Bezeroaren kodea

Zerbitzu bati eskaera bat bidaltzeko, mezularitza-eskaeraren APIra deitu dezakezu bezeroaren edozein tokitan:

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

Banatutako sistema batean, osagaien konfigurazioa oso desberdina izan daiteke, eta eskaera egiten den unean, baliteke mezularitza oraindik ez abiatzea, edo zerbitzu-kontrolatzailea ez egongo da eskaera zerbitzatzeko prest. Hori dela eta, mezularitzaren erantzuna egiaztatu eta hutsegite kasua kudeatu behar dugu.
Bezeroari ongi bidali ondoren, zerbitzuak erantzun bat edo errore bat jasoko du.
Kudea ditzagun bi kasuak handle_info-n:

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};

Eskaeraren zatikako erantzuna

Hobe da mezu handiak bidaltzea saihestea. Sistema osoaren erantzuna eta funtzionamendu egonkorra horren araberakoak dira. Kontsulta baten erantzunak memoria asko hartzen badu, orduan zatitzea derrigorrezkoa da.

Banatutako aplikazioen eraikuntza-blokeak. Lehen hurbilketa

Hona hemen horrelako kasuen adibide pare bat:

  • Osagaiek datu bitarrak trukatzen dituzte, hala nola fitxategiak. Erantzuna zati txikitan zatitzeak edozein tamainatako fitxategiekin modu eraginkorrean lan egiten laguntzen du eta memoria gainezka ez harrapatzen.
  • Zerrendak. Adibidez, datu-baseko taula handi batetik erregistro guztiak hautatu eta beste osagai batera pasatu behar ditugu.

Horrelako erantzunei lokomotora deitzen diet. Nolanahi ere, 1024 1MB mezu hobeak dira 1GB mezu bakarra baino.

Erlang klusterrean, abantaila gehigarri bat lortzen dugu: truke-puntuan eta sarean karga murriztea, erantzunak berehala bidaltzen baititu hartzailearengana, truke-puntua saihestuz.

Eskaerarekin erantzuna

Hau elkarrizketa-sistemak eraikitzeko RPC ereduaren aldaketa arraroa da.

Banatutako aplikazioen eraikuntza-blokeak. Lehen hurbilketa

Argitaratu-harpidetza (datuen banaketaren zuhaitza)

Gertaerak gidatutako sistemek kontsumitzaileei datuak ematen dizkiete eskuragarri dauden heinean. Hortaz, sistemak bultzatzeko eredurako joera handiagoa dute pull edo poll eredurako baino. Ezaugarri honek baliabideak ez xahutzea ahalbidetzen du, datuak etengabe eskatuz eta itxaronez.
Irudiak gai jakin batera harpidetutako kontsumitzaileei mezu bat banatzeko prozesua erakusten du.

Banatutako aplikazioen eraikuntza-blokeak. Lehen hurbilketa

Eredu hau erabiltzeko adibide klasikoak dira estatuaren banaketa: jokoen mundua ordenagailu jokoetan, trukeei buruzko merkatuko datuak, datu-jarioetan informazio erabilgarria.

Kontuan hartu harpidedun kodea:

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.

Iturburuak mezuaren argitaratze-funtzioari dei diezaioke edozein lekutan:

messaging:publish_message(Exchange, Key, Message).

Exchange - Truke puntuaren izena,
Key β€’ bideratze-gakoa
Mezua - zama erabilgarria

Alderantzikatua argitaratu Harpidetza

Banatutako aplikazioen eraikuntza-blokeak. Lehen hurbilketa

Pub-sub zabalduz, saioa hasteko erosoa den eredua lor dezakezu. Iturri eta kontsumitzaileen multzoa guztiz ezberdina izan daiteke. Irudiak kontsumitzaile bat eta iturri asko dituen kasu bat erakusten du.

Zereginen banaketa eredua

Ia proiektu guztietan, prozesamendu geroratuko zereginak daude, hala nola, txostenak sortzea, jakinarazpenak entregatzea eta hirugarrenen sistemetatik datuak jasotzea. Zeregin horiek egiten dituen sistema baten errendimendua erraz eskalatzen da prozesadoreak gehituz. Prozesadore multzo bat osatzea eta haien artean zereginak uniformeki banatzea besterik ez zaigu geratzen.

Kontuan hartu 3 kudeatzaileen adibidea erabiliz sortzen diren egoerak. Zereginen banaketaren fasean ere, kudeatzaileen banaketaren eta gainezkatzearen bidezkotasunaren galdera sortzen da. Banaketa biribila zuzentasunaren ardura izango da, eta kudeatzaileen gainezka egoera saihesteko, murrizketa bat ezarriko dugu. prefetch_limit. Trantsizio moduetan prefetch_limit ez du baimenduko kudeatzaile batek zeregin guztiak jasotzea.

Mezularitzak ilarak eta prozesatzeko lehentasuna kudeatzen ditu. Prozesadoreek iristen diren heinean jasotzen dituzte zereginak. Zereginak arrakastaz edo huts egin dezake:

  • messaging:ack(Tack) β€’ deitzen da mezua arrakastaz prozesatzen bada
  • messaging:nack(Tack) β€’ larrialdi egoera guztietan deituta. Ataza itzuli ondoren, mezuak beste kudeatzaile bati pasatuko dio.

Banatutako aplikazioen eraikuntza-blokeak. Lehen hurbilketa

Demagun hiru zeregin prozesatzen ari ziren bitartean hutsegite konplexu bat gertatu dela: 1. kudeatzaileak, zeregina jaso ondoren, huts egin duela truke-puntuari ezer jakinarazteko astirik izan gabe. Kasu honetan, truke-puntuak lana beste kudeatzaile bati transferituko dio ack denbora-muga igaro ondoren. 3. kudeatzaileak, arrazoiren batengatik, zeregina bertan behera utzi zuen eta nack bat bidali zuen, ondorioz, zeregina arrakastaz amaitu zuen beste kudeatzaile bati ere pasatu zitzaion.

Aurretiazko laburpena

Banatutako sistemen oinarrizko eraikuntza-blokeak banatu ditugu eta Erlang/Elixir-en erabileraren oinarrizko ulermena lortu dugu.

Oinarrizko txantiloiak konbinatuz, paradigma konplexuak eraiki daitezke sortzen ari diren arazoak konpontzeko.

Zikloaren azken zatian, zerbitzuak antolatzeko, bideratze eta orekatzeko gai orokorrak aztertuko ditugu, eta sistemen eskalagarritasunaren eta akatsen tolerantziaren alde praktikoari buruz ere hitz egingo dugu.

Bigarren zatiaren amaiera.

Photo Shoot Marius Christensen
Ilustrazioak websequencediagrams.com-ren eskutik

Iturria: www.habr.com

Gehitu iruzkin berria