Byggingareiningar dreifðra forrita. Fyrsta nálgun

Byggingareiningar dreifðra forrita. Fyrsta nálgun

Í fortíðinni grein Við skoðuðum fræðilegan grunn viðbragðsarkitektúrs. Það er kominn tími til að tala um gagnaflæði, leiðir til að innleiða hvarfgjörn Erlang/Elixir kerfi og skilaboðamynstur í þau:

  • Beiðni-svar
  • Request-Chunked Response
  • Svar með beiðni
  • Birta-gerast áskrifandi
  • Inverted Publish-gerast áskrifandi
  • Verkefnadreifing

SOA, MSA og skilaboð

SOA, MSA eru kerfisarkitektúrar sem skilgreina reglur um byggingarkerfi, á meðan skilaboð veita frumstæður fyrir innleiðingu þeirra.

Ég vil ekki kynna þennan eða hinn kerfisarkitektúr. Ég er fyrir að nota skilvirkustu og gagnlegustu vinnubrögðin fyrir tiltekið verkefni og fyrirtæki. Hvaða hugmyndafræði sem við veljum, þá er betra að búa til kerfisblokkir með auga á Unix-leiðinni: íhlutir með lágmarks tengingu, ábyrgir fyrir einstökum aðilum. API aðferðir framkvæma einföldustu mögulegu aðgerðir með einingum.

Skilaboð eru, eins og nafnið gefur til kynna, skilaboðamiðlari. Megintilgangur þess er að taka á móti og senda skilaboð. Það ber ábyrgð á viðmótum fyrir sendingu upplýsinga, myndun rökrænna rása til að senda upplýsingar innan kerfisins, leiðsögn og jafnvægi, auk bilanameðferðar á kerfisstigi.
Skilaboðin sem við erum að þróa eru ekki að reyna að keppa við eða koma í stað rabbitmq. Helstu eiginleikar þess:

  • Dreifing.
    Hægt er að búa til skiptipunkta á öllum klasahnútum, eins nálægt kóðanum sem notar þá og hægt er.
  • Einfaldleiki.
    Einbeittu þér að því að lágmarka kóðann og auðvelda notkun.
  • Betri árangur.
    Við erum ekki að reyna að endurtaka virkni rabbitmq, heldur varpa ljósi á aðeins byggingar- og flutningslagið, sem við pössum inn í OTP eins einfaldlega og mögulegt er, til að lágmarka kostnað.
  • Sveigjanleiki.
    Hver þjónusta getur sameinað mörg skiptisniðmát.
  • Seiglu með hönnun.
  • Skalanleiki.
    Skilaboð vex með forritinu. Þegar álagið eykst er hægt að færa skiptipunktana yfir á einstakar vélar.

Athugasemd. Hvað varðar skipulag kóða henta metaverkefni vel fyrir flókin Erlang/Elixir kerfi. Allur verkefnakóði er staðsettur í einni geymslu - regnhlífarverkefni. Á sama tíma eru örþjónustur að hámarki einangraðar og framkvæma einfaldar aðgerðir sem bera ábyrgð á sérstakri einingu. Með þessari nálgun er auðvelt að viðhalda API alls kerfisins, það er auðvelt að gera breytingar, það er þægilegt að skrifa eininga- og samþættingarpróf.

Kerfishlutirnir hafa samskipti beint eða í gegnum miðlara. Frá sjónarhóli skilaboða hefur hver þjónusta nokkur lífsstig:

  • Uppsetning þjónustu.
    Á þessu stigi er ferlið sem framkvæmir þjónustuna og ósjálfstæði hennar stillt og ræst.
  • Að búa til skiptipunkt.
    Þjónustan getur notað kyrrstæðan skiptipunkt sem tilgreindur er í hnútstillingunni, eða búið til skiptipunkta á virkan hátt.
  • Þjónustuskráning.
    Til þess að þjónustan geti sinnt beiðnum þarf hún að vera skráð á skiptistöðinni.
  • Venjuleg virkni.
    Þjónustan skilar gagnlegri vinnu.
  • Verklok.
    Það eru 2 tegundir af lokun mögulegar: venjuleg og neyðartilvik. Við venjulega notkun er þjónustan aftengd við skiptistöðina og stöðvast. Í neyðartilvikum keyra skilaboð eitt af bilunarforskriftunum.

Það lítur frekar flókið út en kóðinn er ekki svo ógnvekjandi. Kóðadæmi með athugasemdum verða gefin við greiningu á sniðmátum aðeins síðar.

Ungmennaskipti

Exchange point er skilaboðaferli sem útfærir rökfræði samskipta við hluti innan skilaboðasniðmátsins. Í öllum dæmunum sem kynnt eru hér að neðan hafa íhlutirnir samskipti í gegnum skiptipunkta, samsetning þeirra myndar skilaboð.

Skilaboðaskiptamynstur (MEPs)

Á heimsvísu má skipta skiptamynstri í tvíhliða og einstefnu. Hið fyrra felur í sér svar við skilaboðum sem berast, hið síðarnefnda ekki. Klassískt dæmi um tvíhliða mynstur í arkitektúr viðskiptavinar-miðlara er Request-response mynstur. Við skulum skoða sniðmátið og breytingar á því.

Beiðni-svar eða RPC

RPC er notað þegar við þurfum að fá svar frá öðru ferli. Þetta ferli gæti verið í gangi á sama hnút eða staðsett í annarri heimsálfu. Hér að neðan er skýringarmynd af samskiptum viðskiptavinar og netþjóns í gegnum skilaboð.

Byggingareiningar dreifðra forrita. Fyrsta nálgun

Þar sem skilaboð eru algjörlega ósamstilltur, fyrir viðskiptavininn er skipti skipt í 2 áfanga:

  1. að senda beiðni

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

    skipti ‒ einstakt heiti skiptipunktsins
    ResponseMatchingTag ‒ staðbundið merki fyrir vinnslu svarsins. Til dæmis, þegar um er að ræða að senda nokkrar eins beiðnir sem tilheyra mismunandi notendum.
    RequestDefinition - beiðni aðili
    HandlerProcess ‒ PID stjórnanda. Þetta ferli mun fá svar frá þjóninum.

  2. Vinnur við svarið

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

    SvarPayload - svar miðlara.

Fyrir netþjóninn samanstendur ferlið einnig af 2 áföngum:

  1. Uppsetning skiptipunktsins
  2. Afgreiðsla móttekinna beiðna

Við skulum sýna þetta sniðmát með kóða. Segjum að við þurfum að innleiða einfalda þjónustu sem veitir eina nákvæma tímaaðferð.

Kóði miðlara

Við skulum skilgreina þjónustu API í 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{}
}).

Við skulum skilgreina þjónustustýringuna í 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.

Kóði viðskiptavinar

Til að senda beiðni til þjónustunnar geturðu hringt í skilaboðabeiðni API hvar sem er í biðlaranum:

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

Í dreifðu kerfi getur uppsetning íhluta verið mjög mismunandi og á þeim tíma sem beiðnin er send geta skilaboð ekki enn hafist eða þjónustustjórinn er ekki tilbúinn til að þjóna beiðninni. Þess vegna þurfum við að athuga skilaboðaviðbrögðin og takast á við bilunarmálið.
Eftir árangursríka sendingu mun viðskiptavinurinn fá svar eða villu frá þjónustunni.
Við skulum afgreiða bæði mál í handle_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};

Request-Chunked Response

Það er best að forðast að senda stór skilaboð. Svörun og stöðugur gangur alls kerfisins veltur á þessu. Ef svarið við fyrirspurn tekur mikið minni, þá er nauðsynlegt að skipta því í hluta.

Byggingareiningar dreifðra forrita. Fyrsta nálgun

Leyfðu mér að gefa þér nokkur dæmi um slík tilvik:

  • Íhlutirnir skiptast á tvöföldum gögnum, svo sem skrám. Að skipta svarinu í litla hluta hjálpar þér að vinna á skilvirkan hátt með skrár af hvaða stærð sem er og forðast að flæða yfir minni.
  • Skráningar. Til dæmis þurfum við að velja allar færslur úr risastórri töflu í gagnagrunninum og flytja þær yfir í annan íhlut.

Ég kalla þessi viðbrögð eimreið. Hvað sem því líður eru 1024 skilaboð af 1 MB betri en 1 GB skilaboð.

Í Erlang klasanum fáum við viðbótarávinning - að draga úr álagi á skiptipunktinn og netið, þar sem svör eru send strax til viðtakandans, framhjá skiptipunktinum.

Svar með beiðni

Þetta er frekar sjaldgæf breyting á RPC mynstrinu til að byggja upp gluggakerfi.

Byggingareiningar dreifðra forrita. Fyrsta nálgun

Birta-gerast áskrifandi (gagnadreifingartré)

Atburðadrifin kerfi skila þeim til neytenda um leið og gögnin eru tilbúin. Þannig eru kerfi líklegri til að nota ýta líkan en að draga eða skoða líkan. Þessi eiginleiki gerir þér kleift að forðast að sóa auðlindum með því að biðja stöðugt um og bíða eftir gögnum.
Myndin sýnir ferlið við að dreifa skilaboðum til neytenda sem eru áskrifendur að tilteknu efni.

Byggingareiningar dreifðra forrita. Fyrsta nálgun

Klassísk dæmi um að nota þetta mynstur eru dreifing ríkisins: leikjaheimurinn í tölvuleikjum, markaðsgögn um kauphallir, gagnlegar upplýsingar í gagnastraumum.

Við skulum skoða áskrifendakóðann:

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.

Heimildarmaðurinn getur kallað aðgerðina til að birta skilaboð á hverjum hentugum stað:

messaging:publish_message(Exchange, Key, Message).

skipti - nafn skiptistaðar,
Key - leiðarlykill
skilaboðin - farmur

Inverted Publish-gerast áskrifandi

Byggingareiningar dreifðra forrita. Fyrsta nálgun

Með því að stækka pub-sub geturðu fengið mynstur sem er þægilegt fyrir skógarhögg. Heimildir og neytendur geta verið allt öðruvísi. Myndin sýnir mál með einum neytanda og mörgum heimildum.

Dreifingarmynstur verkefna

Næstum hvert verkefni felur í sér frestað vinnsluverkefni, svo sem að búa til skýrslur, senda tilkynningar og sækja gögn úr kerfum þriðja aðila. Auðvelt er að stækka afköst kerfisins sem sinnir þessum verkefnum með því að bæta við meðhöndlum. Það eina sem er eftir fyrir okkur er að mynda hóp af örgjörvum og dreifa verkefnum jafnt á milli þeirra.

Við skulum skoða aðstæðurnar sem koma upp með því að nota dæmið um 3 meðhöndlunarmenn. Jafnvel á stigi verkefnadreifingar vaknar spurningin um sanngjarna dreifingu og yfirflæði umsjónarmanna. Dreifing í hringrás mun bera ábyrgð á sanngirni og til að koma í veg fyrir að það fari yfir flæði á meðhöndlunaraðilum munum við setja takmörkun prefetch_limit. Við tímabundnar aðstæður prefetch_limit kemur í veg fyrir að einn stjórnandi fái öll verkefni.

Skilaboð stjórnar biðröðum og vinnsluforgangi. Örgjörvar fá verkefni um leið og þau koma. Verkefnið getur lokið með góðum árangri eða mistókst:

  • messaging:ack(Tack) - hringt ef tekist hefur að vinna úr skilaboðunum
  • messaging:nack(Tack) - hringt í allar neyðartilvik. Þegar verkefninu er skilað munu skilaboð senda það til annars meðhöndlunar.

Byggingareiningar dreifðra forrita. Fyrsta nálgun

Segjum að flókin bilun hafi átt sér stað við vinnslu þriggja verkefna: örgjörvi 1, eftir að hafa fengið verkefnið, hrundi án þess að hafa tíma til að tilkynna neitt til skiptistöðvarinnar. Í þessu tilviki mun skiptipunkturinn flytja verkefnið til annars meðhöndlunar eftir að ack timeout er útrunnið. Einhverra hluta vegna yfirgaf stjórnandi 3 verkefnið og sendi nack; í kjölfarið var verkefnið einnig flutt til annars stjórnanda sem kláraði það með góðum árangri.

Bráðabirgðayfirlit

Við höfum farið yfir helstu byggingareiningar dreifðra kerfa og öðlast grunnskilning á notkun þeirra í Erlang/Elixir.

Með því að sameina grunnmynstur geturðu byggt upp flóknar hugmyndafræði til að leysa vandamál sem koma upp.

Í lokahluta seríunnar munum við skoða almenn atriði varðandi skipulagningu þjónustu, leiðarlýsingu og jafnvægi, og einnig verður fjallað um hagnýtu hliðina á sveigjanleika og bilanaþoli kerfa.

Lok seinni hlutans.

Photo Shoot Marius Christensen
Teikningar unnar með því að nota websequencediagrams.com

Heimild: www.habr.com

Bæta við athugasemd