Blociau adeiladu o geisiadau a ddosbarthwyd. Dull cyntaf

Blociau adeiladu o geisiadau a ddosbarthwyd. Dull cyntaf

Yn yr olaf Erthygl Gwnaethom archwilio sylfeini damcaniaethol pensaernïaeth adweithiol. Mae'n bryd siarad am lif data, ffyrdd o weithredu systemau Erlang/Elixir adweithiol a phatrymau negeseuon ynddynt:

  • Cais-ymateb
  • Ymateb Cais wedi'i Gyfri
  • Ymateb gyda'r Cais
  • Cyhoeddi-tanysgrifio
  • Inverted Cyhoeddi-danysgrifio
  • Dosbarthiad tasg

SOA, MSA a Negeseuon

Mae SOA, MSA yn saernïaeth system sy'n diffinio'r rheolau ar gyfer adeiladu systemau, tra bod negeseuon yn darparu cyntefigau ar gyfer eu gweithredu.

Nid wyf am hyrwyddo hyn na'r bensaernïaeth system honno. Rwyf am ddefnyddio'r arferion mwyaf effeithiol a defnyddiol ar gyfer prosiect a busnes penodol. Pa bynnag batrwm a ddewiswn, mae'n well creu blociau system gyda llygad ar y ffordd Unix: cydrannau â chysylltedd lleiaf posibl, sy'n gyfrifol am endidau unigol. Mae dulliau API yn cyflawni'r gweithredoedd symlaf posibl gydag endidau.

Mae negeseuon, fel y mae'r enw'n awgrymu, yn frocer negeseuon. Ei brif bwrpas yw derbyn ac anfon negeseuon. Mae'n gyfrifol am y rhyngwynebau ar gyfer anfon gwybodaeth, ffurfio sianeli rhesymegol ar gyfer trosglwyddo gwybodaeth o fewn y system, llwybro a chydbwyso, yn ogystal â thrin namau ar lefel y system.
Nid yw'r negeseuon yr ydym yn eu datblygu yn ceisio cystadlu â rabbitmq na'i ddisodli. Ei brif nodweddion:

  • Dosbarthiad.
    Gellir creu pwyntiau cyfnewid ar bob nod clwstwr, mor agos â phosibl at y cod sy'n eu defnyddio.
  • Symlrwydd.
    Canolbwyntiwch ar leihau cod boilerplate a rhwyddineb defnydd.
  • Gwell perfformiad.
    Nid ydym yn ceisio ailadrodd ymarferoldeb rabbitmq, ond yn tynnu sylw at yr haen bensaernïol a thrafnidiaeth yn unig, yr ydym yn ei ffitio yn yr OTP mor syml â phosibl, gan leihau costau.
  • Hyblygrwydd.
    Gall pob gwasanaeth gyfuno llawer o dempledi cyfnewid.
  • Gwydnwch trwy ddyluniad.
  • Scalability.
    Mae negeseuon yn tyfu gyda'r cais. Wrth i'r llwyth gynyddu, gallwch chi symud y pwyntiau cyfnewid i beiriannau unigol.

Sylw. O ran trefniadaeth cod, mae meta-brosiectau yn addas iawn ar gyfer systemau Erlang/Elixir cymhleth. Mae holl god y prosiect wedi'i leoli mewn un ystorfa - prosiect ymbarél. Ar yr un pryd, mae microwasanaethau yn cael eu hynysu i'r eithaf ac yn perfformio gweithrediadau syml sy'n gyfrifol am endid ar wahân. Gyda'r dull hwn, mae'n hawdd cynnal API y system gyfan, mae'n hawdd gwneud newidiadau, mae'n gyfleus ysgrifennu profion uned ac integreiddio.

Mae cydrannau'r system yn rhyngweithio'n uniongyrchol neu drwy frocer. O safbwynt negeseuon, mae gan bob gwasanaeth sawl cyfnod bywyd:

  • Cychwyn gwasanaeth.
    Ar y cam hwn, mae'r broses a'r dibyniaethau sy'n gweithredu'r gwasanaeth yn cael eu ffurfweddu a'u lansio.
  • Creu pwynt cyfnewid.
    Gall y gwasanaeth ddefnyddio pwynt cyfnewid statig a bennir yn y cyfluniad nod, neu greu pwyntiau cyfnewid yn ddeinamig.
  • Cofrestru gwasanaeth.
    Er mwyn i'r gwasanaeth gyflwyno ceisiadau, rhaid ei gofrestru yn y man cyfnewid.
  • Gweithrediad arferol.
    Mae'r gwasanaeth yn cynhyrchu gwaith defnyddiol.
  • Cau i lawr.
    Mae 2 fath o gau i lawr yn bosibl: arferol ac argyfwng. Yn ystod gweithrediad arferol, mae'r gwasanaeth yn cael ei ddatgysylltu o'r pwynt cyfnewid ac yn stopio. Mewn sefyllfaoedd brys, mae negeseuon yn gweithredu un o'r sgriptiau methu.

Mae'n edrych yn eithaf cymhleth, ond nid yw'r cod mor frawychus i gyd â hynny. Rhoddir enghreifftiau cod gyda sylwadau yn y dadansoddiad o dempledi ychydig yn ddiweddarach.

Cyfnewid

Proses negeseuon yw pwynt cyfnewid sy'n gweithredu rhesymeg rhyngweithio â chydrannau o fewn y templed negeseuon. Yn yr holl enghreifftiau a gyflwynir isod, mae'r cydrannau'n rhyngweithio trwy bwyntiau cyfnewid, y mae'r cyfuniad ohonynt yn ffurfio negeseuon.

Patrymau cyfnewid negeseuon (ASE)

Yn fyd-eang, gellir rhannu patrymau cyfnewid yn ddwy ffordd ac un ffordd. Mae'r cyntaf yn awgrymu ymateb i neges sy'n dod i mewn, nid yw'r olaf yn awgrymu. Enghraifft glasurol o batrwm dwy ffordd mewn pensaernïaeth cleient-gweinydd yw'r patrwm Cais-ymateb. Gadewch i ni edrych ar y templed a'i addasiadau.

Cais-ymateb neu RPC

Defnyddir RPC pan fydd angen inni dderbyn ymateb o broses arall. Gall y broses hon fod yn rhedeg ar yr un nod neu wedi'i lleoli ar gyfandir gwahanol. Isod mae diagram o'r rhyngweithio rhwng cleient a gweinydd trwy negeseuon.

Blociau adeiladu o geisiadau a ddosbarthwyd. Dull cyntaf

Gan fod negeseuon yn gwbl anghydamserol, ar gyfer y cleient mae'r cyfnewid wedi'i rannu'n 2 gam:

  1. Yn anfon cais

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

    cyfnewid ‒ enw unigryw y pwynt cyfnewid
    ResponseMatchingTag ‒ label lleol ar gyfer prosesu'r ymateb. Er enghraifft, yn achos anfon nifer o geisiadau union yr un fath yn perthyn i wahanol ddefnyddwyr.
    CaisDiffiniad - corff cais
    TriniwrProcess ‒ PID y triniwr. Bydd y broses hon yn derbyn ymateb gan y gweinydd.

  2. Prosesu'r ymateb

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

    Llwyth Ymateb - ymateb gweinydd.

Ar gyfer y gweinydd, mae'r broses hefyd yn cynnwys 2 gam:

  1. Cychwyn y pwynt cyfnewid
  2. Prosesu ceisiadau a dderbyniwyd

Gadewch i ni ddarlunio'r templed hwn gyda chod. Gadewch i ni ddweud bod angen inni weithredu gwasanaeth syml sy'n darparu un dull union amser.

Cod gweinydd

Gadewch i ni ddiffinio'r API gwasanaeth yn 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{}
}).

Gadewch i ni ddiffinio'r rheolydd gwasanaeth yn 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.

Cod cleient

Er mwyn anfon cais i'r gwasanaeth, gallwch ffonio'r API cais negeseuon unrhyw le yn y cleient:

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

Mewn system ddosbarthedig, gall cyfluniad cydrannau fod yn wahanol iawn ac ar adeg y cais, efallai na fydd negeseuon yn dechrau eto, neu ni fydd rheolwr y gwasanaeth yn barod i gyflwyno'r cais. Felly, mae angen inni wirio'r ymateb negeseuon a thrin yr achos methiant.
Ar ôl ei anfon yn llwyddiannus, bydd y cleient yn derbyn ymateb neu gamgymeriad gan y gwasanaeth.
Gadewch i ni drin y ddau achos yn 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};

Ymateb Cais wedi'i Gyfri

Mae'n well osgoi anfon negeseuon enfawr. Mae ymatebolrwydd a gweithrediad sefydlog y system gyfan yn dibynnu ar hyn. Os yw'r ymateb i ymholiad yn cymryd llawer o gof, yna mae'n orfodol ei rannu'n rhannau.

Blociau adeiladu o geisiadau a ddosbarthwyd. Dull cyntaf

Gadewch imi roi ychydig o enghreifftiau ichi o achosion o'r fath:

  • Mae'r cydrannau'n cyfnewid data deuaidd, megis ffeiliau. Mae torri'r ymateb yn rhannau bach yn eich helpu i weithio'n effeithlon gyda ffeiliau o unrhyw faint ac osgoi gorlifoedd cof.
  • Rhestrau. Er enghraifft, mae angen i ni ddewis pob cofnod o dabl enfawr yn y gronfa ddata a'u trosglwyddo i gydran arall.

Rwy'n galw'r locomotif ymatebion hyn. Beth bynnag, mae 1024 o negeseuon o 1 MB yn well na neges sengl o 1 GB.

Yn y clwstwr Erlang, rydym yn cael budd ychwanegol - lleihau'r llwyth ar y pwynt cyfnewid a'r rhwydwaith, gan fod ymatebion yn cael eu hanfon ar unwaith at y derbynnydd, gan osgoi'r pwynt cyfnewid.

Ymateb gyda'r Cais

Mae hwn yn addasiad prin iawn o'r patrwm RPC ar gyfer adeiladu systemau deialog.

Blociau adeiladu o geisiadau a ddosbarthwyd. Dull cyntaf

Cyhoeddi-tanysgrifio (coeden dosbarthu data)

Mae systemau sy'n cael eu gyrru gan ddigwyddiadau yn eu danfon i ddefnyddwyr cyn gynted ag y bydd y data'n barod. Felly, mae systemau yn fwy tueddol o fodel gwthio nag i fodel tynnu neu bleidleisio. Mae'r nodwedd hon yn caniatáu ichi osgoi gwastraffu adnoddau trwy ofyn ac aros am ddata yn gyson.
Mae'r ffigur yn dangos y broses o ddosbarthu neges i ddefnyddwyr sydd wedi tanysgrifio i bwnc penodol.

Blociau adeiladu o geisiadau a ddosbarthwyd. Dull cyntaf

Enghreifftiau clasurol o ddefnyddio'r patrwm hwn yw dosbarthiad y wladwriaeth: y byd gêm mewn gemau cyfrifiadurol, data marchnad ar gyfnewidfeydd, gwybodaeth ddefnyddiol mewn porthiannau data.

Edrychwn ar y cod tanysgrifiwr:

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.

Gall y ffynhonnell alw'r swyddogaeth i gyhoeddi neges mewn unrhyw le cyfleus:

messaging:publish_message(Exchange, Key, Message).

cyfnewid - enw'r pwynt cyfnewid,
allweddol - allwedd llwybro
Neges - llwyth tâl

Inverted Cyhoeddi-danysgrifio

Blociau adeiladu o geisiadau a ddosbarthwyd. Dull cyntaf

Trwy ehangu tafarn-is, gallwch gael patrwm cyfleus ar gyfer logio. Gall y set o ffynonellau a defnyddwyr fod yn hollol wahanol. Mae'r ffigur yn dangos achos gydag un defnyddiwr a ffynonellau lluosog.

Patrwm dosbarthu tasgau

Mae bron pob prosiect yn cynnwys tasgau prosesu gohiriedig, megis cynhyrchu adroddiadau, cyflwyno hysbysiadau, ac adalw data o systemau trydydd parti. Gellir graddio trwygyrch y system sy'n cyflawni'r tasgau hyn yn hawdd trwy ychwanegu trinwyr. Y cyfan sydd ar ôl i ni yw ffurfio clwstwr o broseswyr a dosbarthu tasgau’n gyfartal rhyngddynt.

Gadewch i ni edrych ar y sefyllfaoedd sy'n codi gan ddefnyddio'r enghraifft o 3 triniwr. Hyd yn oed ar y cam o ddosbarthu tasgau, mae'r cwestiwn o degwch y dosbarthiad a gorlif y trinwyr yn codi. Bydd dosbarthiad robin crwn yn gyfrifol am degwch, ac er mwyn osgoi sefyllfa o orlif o drinwyr, byddwn yn cyflwyno cyfyngiad prefetch_limit. Mewn amodau dros dro prefetch_limit yn atal un triniwr rhag derbyn yr holl dasgau.

Mae negeseuon yn rheoli ciwiau a blaenoriaeth prosesu. Mae proseswyr yn derbyn tasgau wrth iddynt gyrraedd. Gall y dasg gwblhau'n llwyddiannus neu fethu:

  • messaging:ack(Tack) - wedi'i alw os yw'r neges yn cael ei phrosesu'n llwyddiannus
  • messaging:nack(Tack) - yn cael ei alw ym mhob sefyllfa o argyfwng. Unwaith y bydd y dasg wedi'i dychwelyd, bydd negeseuon yn ei throsglwyddo i driniwr arall.

Blociau adeiladu o geisiadau a ddosbarthwyd. Dull cyntaf

Tybiwch fod methiant cymhleth wedi digwydd wrth brosesu tair tasg: damwain prosesydd 1, ar ôl derbyn y dasg, heb gael amser i adrodd am unrhyw beth i'r pwynt cyfnewid. Yn yr achos hwn, bydd y pwynt cyfnewid yn trosglwyddo'r dasg i driniwr arall ar ôl i'r terfyn amser ddod i ben. Am ryw reswm, gadawodd triniwr 3 y dasg ac anfon noeth; o ganlyniad, trosglwyddwyd y dasg hefyd i driniwr arall a'i cwblhaodd yn llwyddiannus.

Crynodeb rhagarweiniol

Rydym wedi ymdrin â blociau adeiladu sylfaenol systemau gwasgaredig ac wedi ennill dealltwriaeth sylfaenol o'u defnydd yn Erlang/Elixir.

Trwy gyfuno patrymau sylfaenol, gallwch adeiladu patrymau cymhleth i ddatrys problemau sy'n dod i'r amlwg.

Yn rhan olaf y gyfres, byddwn yn edrych ar faterion cyffredinol trefnu gwasanaethau, llwybro a chydbwyso, a hefyd yn siarad am ochr ymarferol scalability a goddefgarwch diffygion systemau.

Diwedd yr ail ran.

Shoot Photo Marius Christensen
Lluniau wedi'u paratoi gan ddefnyddio websequencediagrams.com

Ffynhonnell: hab.com

Ychwanegu sylw