Бөлүштүрүлгөн колдонмолордун курулуш блоктору. Биринчи мамиле

Бөлүштүрүлгөн колдонмолордун курулуш блоктору. Биринчи мамиле

Акырында макала Биз реактивдүү архитектуранын теориялык негиздерин карап чыктык. Маалымат агымдары, реактивдүү Erlang/Elixir системаларын ишке ашыруу жолдору жана алардагы билдирүүлөрдүн үлгүлөрү жөнүндө сөз кылууга убакыт келди:

  • Суроо-жооп
  • Суроо-талапка бөлүнгөн жооп
  • Суроо менен жооп
  • Жарыялоо-жазылуу
  • Inverted Publish-жазылуу
  • Тапшырма бөлүштүрүү

SOA, MSA жана Messaging

SOA, MSA - бул системаларды куруу эрежелерин аныктаган система архитектурасы, ал эми билдирүүлөр аларды ишке ашыруу үчүн примитивдерди камсыз кылат.

Тигил же бул системанын архитектурасын илгерилетким келбейт. Мен белгилүү бир долбоор жана бизнес үчүн эң эффективдүү жана пайдалуу тажрыйбаларды колдонууну жактайм. Кайсы парадигманы тандабайлы, Unix-жолуна көз салуу менен системалык блокторду түзүү жакшыраак: минималдуу байланышы бар компоненттер, жеке жактар ​​үчүн жооптуу. API ыкмалары объекттер менен мүмкүн болгон эң жөнөкөй аракеттерди аткарат.

Кабарлашуу, аты айтып тургандай, билдирүү брокери. Анын негизги максаты билдирүүлөрдү кабыл алуу жана жөнөтүү болуп саналат. Ал маалыматты жөнөтүү үчүн интерфейстерге, системанын ичинде маалыматты берүү үчүн логикалык каналдарды калыптандырууга, маршрутташтырууга жана балансташтырууга, ошондой эле система деңгээлинде каталарды башкарууга жооп берет.
Биз иштеп жаткан кабарлашуу rabbitmq менен атаандашууга же алмаштырууга аракет кылбайт. Анын негизги өзгөчөлүктөрү:

  • Бөлүштүрүү.
    Алмашуу пункттары бардык кластердик түйүндөрдө, аларды колдонгон кодго мүмкүн болушунча жакын түзүлүшү мүмкүн.
  • Жөнөкөйлүк.
    Кодду минималдаштырууга жана колдонуунун оңойлугуна көңүл буруңуз.
  • Жакшыраак аткаруу.
    Биз rabbitmq функционалдуулугун кайталаганга аракет кылбайбыз, бирок чыгымдарды азайтып, OTPге мүмкүн болушунча жөнөкөй түрдө туура келген архитектуралык жана транспорттук катмарды гана бөлүп көрсөтөбүз.
  • Ийкемдүүлүк.
    Ар бир кызмат көптөгөн алмашуу шаблондорун бириктире алат.
  • Дизайн боюнча ийкемдүүлүк.
  • Масштабдуулук.
    Кабарлашуу колдонмо менен өсөт. Жүктүн көбөйүшү менен, алмашуу пункттарын айрым машиналарга жылдыра аласыз.

Эскертүү. Кодду уюштуруу жагынан мета-долбоорлор татаал Erlang/Elixir системалары үчүн абдан ылайыктуу. Бардык долбоордун коду бир репозиторийде жайгашкан - кол чатыр долбоор. Ошол эле учурда, микросервистер максималдуу түрдө обочолонуп, өзүнчө объект үчүн жооптуу болгон жөнөкөй операцияларды аткарышат. Бул ыкма менен бүт системанын API'син сактоо оңой, өзгөртүү киргизүү оңой, бирдикти жана интеграциялык тесттерди жазуу ыңгайлуу.

Системанын компоненттери түздөн-түз же брокер аркылуу өз ара аракеттенет. Кабарлашуу көз карашынан алганда, ар бир кызматтын бир нече жашоо фазалары бар:

  • Кызматты инициализациялоо.
    Бул этапта процесс жана кызматты аткаруучу көз карандылыктар конфигурацияланат жана ишке киргизилет.
  • алмашуу пунктун түзүү.
    Кызмат түйүн конфигурациясында көрсөтүлгөн статикалык алмашуу пунктун колдоно алат же динамикалык алмашуу пункттарын түзө алат.
  • Кызматты каттоо.
    Кызмат суроо-талаптарды тейлөө үчүн, ал алмашуу пунктунда катталышы керек.
  • Нормалдуу иштеши.
    Кызмат пайдалуу иштерди жаратат.
  • Өчүрүү.
    Өчүрүүнүн 2 түрү бар: кадимки жана авариялык. Кадимки иштөө учурунда кызмат алмашуу пунктунан ажыратылып, токтойт. Өзгөчө кырдаалдарда билдирүү алмашуу скрипттердин бирин аткарат.

Бул абдан татаал көрүнөт, бирок код баары коркунучтуу эмес. Комментарийлер менен коддуу мисалдар шаблондорду талдоодо бир аз кийинчерээк берилет.

алмашуу

Алмашуу пункту – билдирүү шаблонунун ичиндеги компоненттер менен өз ара аракеттенүү логикасын ишке ашырган билдирүү процесси. Төмөндө келтирилген бардык мисалдарда компоненттер алмашуу пункттары аркылуу өз ара аракеттенишет, алардын айкалышы билдирүүлөрдү түзүшөт.

Билдирүү алмашуу үлгүлөрү (МПР)

Бүткүл дүйнөлүк деңгээлде алмашуу моделдерин эки тараптуу жана бир тараптуу деп бөлүүгө болот. Биринчиси келген билдирүүгө жоопту билдирет, экинчиси андай эмес. Кардар-сервер архитектурасындагы эки тараптуу үлгүнүн классикалык үлгүсү Суроо-жооп үлгүсү болуп саналат. Келгиле, шаблонду жана анын өзгөртүүлөрүн карап көрөлү.

Суроо-жооп же RPC

RPC башка процесстен жооп алышыбыз керек болгондо колдонулат. Бул процесс бир эле түйүндө же башка континентте жайгашкан болушу мүмкүн. Төмөндө билдирүү аркылуу кардар менен сервердин өз ара аракеттенүүсүнүн диаграммасы келтирилген.

Бөлүштүрүлгөн колдонмолордун курулуш блоктору. Биринчи мамиле

Кабарлашуу толугу менен асинхрондук болгондуктан, кардар үчүн алмашуу 2 фазага бөлүнөт:

  1. Сурам жөнөтүү

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

    алмашуу ‒ алмашуу пунктунун уникалдуу аталышы
    ResponseMatchingTag ‒ жоопту иштетүү үчүн жергиликтүү белги. Мисалы, ар кандай колдонуучуларга тиешелүү бир нече окшош суроо-талаптарды жөнөтүүдө.
    RequestDefinition - суроо-талап органы
    HandlerProcess ‒ Иштөөчүнүн PID. Бул процесс серверден жооп алат.

  2. Жооп иштетилүүдө

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

    ResponsePayload - сервердин жообу.

Сервер үчүн процесс 2 этаптан турат:

  1. Алмашуу пунктун баштоо
  2. Кабыл алынган суроо-талаптарды иштетүү

Келгиле, бул шаблонду код менен сүрөттөп көрөлү. Бир так убакыт ыкмасын камсыз кылган жөнөкөй кызматты ишке ашыруу керек дейли.

Сервер коду

api.hrl ичинде API кызматын аныктайлы:

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

Кызмат контроллерин 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.

Кардар коду

Кызматка суроо-талап жөнөтүү үчүн, сиз кардардын каалаган жеринде билдирүү сурамынын API'сине чалсаңыз болот:

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

Бөлүштүрүлгөн системада компоненттердин конфигурациясы өтө ар түрдүү болушу мүмкүн жана суроо-талап учурунда билдирүүлөр али башталбай калышы мүмкүн же кызмат контроллери суроо-талапты тейлөөгө даяр эмес. Ошондуктан, биз билдирүү жообун текшерип, ийгиликсиздикти чечүүбүз керек.
Ийгиликтүү жөнөтүүдөн кийин кардар кызматтан жооп же ката алат.
Келгиле, 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};

Суроо-талапка бөлүнгөн жооп

Чоң билдирүүлөрдү жөнөтүүдөн качканыңыз жакшы. Бүткүл системанын жооп берүү жөндөмдүүлүгү жана туруктуу иштеши ушундан көз каранды. Эгерде суроого жооп көп эстутумду ээлесе, анда аны бөлүктөргө бөлүү милдеттүү болуп саналат.

Бөлүштүрүлгөн колдонмолордун курулуш блоктору. Биринчи мамиле

Мындай учурларга бир нече мисал келтирейин:

  • Компоненттер файлдар сыяктуу бинардык маалыматтарды алмашат. Жоопту майда бөлүктөргө бөлүү каалаган өлчөмдөгү файлдар менен эффективдүү иштөөгө жана эс тутумдун толуп кетишинин алдын алууга жардам берет.
  • Listings. Мисалы, биз базадагы чоң таблицадан бардык жазууларды тандап, башка компонентке өткөрүп беришибиз керек.

Мен бул жоопторду локомотив деп атайм. Кандай болгон күндө да, 1024 ГБ көлөмүндөгү 1 билдирүү 1 ГБ бир билдирүүгө караганда жакшыраак.

Erlang кластеринде биз кошумча пайда алабыз - алмашуу пунктуна жана тармакка жүктөмдү азайтабыз, анткени жооптор алмашуу пунктун айланып өтүп, дароо алуучуга жөнөтүлөт.

Суроо менен жооп

Бул диалог системаларын куруу үчүн RPC үлгүсүнүн өтө сейрек модификациясы.

Бөлүштүрүлгөн колдонмолордун курулуш блоктору. Биринчи мамиле

Жарыялоо-жазылуу (маалыматтарды бөлүштүрүү дарагы)

Окуяга негизделген системалар аларды керектөөчүлөргө маалымат даяр болгондон кийин жеткирет. Ошентип, системалар тартуу же сурамжылоо моделине караганда түртүү моделине көбүрөөк жакын болушат. Бул функция маалыматтарды тынымсыз суроо жана күтүү аркылуу ресурстарды текке кетирбөөгө мүмкүндүк берет.
Сүрөт белгилүү бир темага жазылган керектөөчүлөргө билдирүү таратуу процессин көрсөтөт.

Бөлүштүрүлгөн колдонмолордун курулуш блоктору. Биринчи мамиле

Бул үлгүнү колдонуунун классикалык мисалдары абалды бөлүштүрүү болуп саналат: компьютердик оюндардагы оюн дүйнөсү, биржалардагы рыноктук маалыматтар, маалымат ленталарында пайдалуу маалымат.

Келгиле, абоненттин кодун карап көрөлү:

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.

Булак каалаган ыңгайлуу жерде билдирүү жарыялоо үчүн функцияны чакыра алат:

messaging:publish_message(Exchange, Key, Message).

алмашуу - алмашуу пунктунун аталышы,
ачкыч - маршруттук ачкыч
билдирүү - пайдалуу жүк

Inverted Publish-жазылуу

Бөлүштүрүлгөн колдонмолордун курулуш блоктору. Биринчи мамиле

Pub-sub кеңейтүү менен, сиз журналга ыңгайлуу үлгү ала аласыз. булактардын жана керектөөчүлөрдүн жыйындысы такыр башка болушу мүмкүн. Сүрөттө бир керектөөчү жана бир нече булактардан турган иш көрсөтүлгөн.

Тапшырма бөлүштүрүү үлгүсү

Дээрлик ар бир долбоор отчетторду түзүү, эскертмелерди жеткирүү жана үчүнчү тараптын системаларынан маалыматтарды алуу сыяктуу кийинкиге калтырылган иштетүү милдеттерин камтыйт. Бул милдеттерди аткарып жаткан системанын өткөрүү жөндөмдүүлүгүн иштетүүчүлөрдү кошуу менен оңой эле масштабдаса болот. Бизге болгону процессорлордун кластерин түзүү жана алардын ортосунда милдеттерди бирдей бөлүштүрүү гана калды.

Келгиле, 3 иштетүүчүнүн мисалында пайда болгон жагдайларды карап көрөлү. Тапшырмаларды бөлүштүрүү стадиясында да бөлүштүрүүнүн адилеттүүлүгү жана башкаруучулардын толуп кетиши жөнүндө маселе келип чыгат. Калыстык үчүн тегерек бөлүштүрүү жооптуу болот жана башкаруучулардын толуп кетишинин алдын алуу үчүн биз чектөө киргизебиз. prefetch_limit. Убактылуу шарттарда prefetch_limit бир иштеткичке бардык тапшырмаларды алуудан тоскоол болот.

Кабарлашуу кезектерди жана иштетүү артыкчылыктарын башкарат. Процессорлор келгенде тапшырмаларды алышат. Тапшырма ийгиликтүү же аткарылбай калышы мүмкүн:

  • messaging:ack(Tack) - эгер билдирүү ийгиликтүү иштетилсе, чакырды
  • messaging:nack(Tack) - бардык өзгөчө кырдаалдарда чакырылат. Тапшырма кайтарылгандан кийин, билдирүү жөнөтүү аны башка иштетүүчүгө өткөрүп берет.

Бөлүштүрүлгөн колдонмолордун курулуш блоктору. Биринчи мамиле

Үч тапшырманы иштеп чыгууда татаал мүчүлүштүк болду дейли: 1-процессор тапшырманы алгандан кийин, алмашуу пунктуна эч нерсе билдирүүгө үлгүрбөй кыйраган. Бул учурда, алмашуу пункту кабыл алуу күтүү мөөнөтү аяктагандан кийин тапшырманы башка иштеткичке өткөрүп берет. Эмнегедир 3 иштеткич тапшырманы таштап, нака жөнөттү, натыйжада тапшырма дагы аны ийгиликтүү аткарган башка иштетүүчүгө өткөрүлүп берилди.

Алдын ала кыскача маалымат

Биз бөлүштүрүлгөн системалардын негизги курулуш блокторун карап чыктык жана аларды Erlang/Elixirде колдонуу боюнча негизги түшүнүккө ээ болдук.

Негизги үлгүлөрдү айкалыштыруу менен, сиз пайда болгон көйгөйлөрдү чечүү үчүн татаал парадигмаларды түзө аласыз.

Сериянын жыйынтыктоочу бөлүгүндө биз кызматтарды уюштуруунун, маршрутташтыруунун жана балансташтыруунун жалпы маселелерин карап чыгабыз, ошондой эле системалардын масштабдуулугунун жана катачылыкка чыдамдуулугунун практикалык жагы жөнүндө сүйлөшөбүз.

Экинчи бөлүктүн аягы.

Сүрөт Мариус Кристенсен
Иллюстрациялар websequencediagrams.com аркылуу даярдалган

Source: www.habr.com

Комментарий кошуу