Paylanmış proqramların tikinti blokları. İlk yanaşma

Paylanmış proqramların tikinti blokları. İlk yanaşma

Sonda məqalə Reaktiv arxitekturanın nəzəri əsaslarını araşdırdıq. Məlumat axınları, reaktiv Erlang/Elixir sistemlərinin tətbiqi yolları və onlarda mesajlaşma nümunələri haqqında danışmağın vaxtıdır:

  • Sorğu-cavab
  • Sorğu-Böyük Cavab
  • Sorğu ilə cavab
  • Nəşr et - abunə ol
  • Ters çevrilmiş Nəşr et-abunə ol
  • Tapşırıq paylanması

SOA, MSA və Mesajlaşma

SOA, MSA sistemlərin qurulması qaydalarını müəyyən edən sistem arxitekturalarıdır, mesajlaşma isə onların həyata keçirilməsi üçün primitivləri təmin edir.

Mən bu və ya digər sistem arxitekturasını təbliğ etmək istəmirəm. Mən konkret layihə və biznes üçün ən effektiv və faydalı təcrübələrdən istifadə etmək tərəfdarıyam. Hansı paradiqmanı seçdiyimizdən asılı olmayaraq, Unix-yolunu nəzərə alaraq sistem bloklarını yaratmaq daha yaxşıdır: minimal əlaqəyə malik komponentlər, fərdi qurumlar üçün cavabdehdir. API metodları obyektlərlə mümkün olan ən sadə hərəkətləri yerinə yetirir.

Mesajlaşma, adından da göründüyü kimi, mesaj brokeridir. Onun əsas məqsədi mesajları qəbul etmək və göndərməkdir. O, məlumatların göndərilməsi üçün interfeyslərə, sistem daxilində məlumatların ötürülməsi üçün məntiqi kanalların formalaşmasına, marşrutlaşdırma və balanslaşdırmaya, həmçinin sistem səviyyəsində nasazlıqların aradan qaldırılmasına cavabdehdir.
Bizim inkişaf etdirdiyimiz mesajlaşma rabbitmq ilə rəqabət aparmağa və ya onu əvəz etməyə çalışmır. Onun əsas xüsusiyyətləri:

  • Paylanma.
    Mübadilə nöqtələri onları istifadə edən koda mümkün qədər yaxın bütün klaster qovşaqlarında yaradıla bilər.
  • Sadəlik.
    Qazan kodunu və istifadə rahatlığını minimuma endirməyə diqqət yetirin.
  • Daha yaxşı performans.
    Biz rabbitmq funksionallığını təkrarlamağa çalışmırıq, ancaq xərcləri minimuma endirərək OTP-yə mümkün qədər sadə şəkildə uyğunlaşdırdığımız memarlıq və nəqliyyat təbəqəsini vurğulayırıq.
  • Çeviklik.
    Hər bir xidmət bir çox mübadilə şablonunu birləşdirə bilər.
  • Dizaynla davamlılıq.
  • Ölçeklenebilirlik.
    Mesajlaşma tətbiq ilə böyüyür. Yük artdıqca, mübadilə nöqtələrini fərdi maşınlara köçürə bilərsiniz.

Qeyd Kod təşkili baxımından meta-layihələr mürəkkəb Erlang/Elixir sistemləri üçün yaxşı uyğun gəlir. Bütün layihə kodu bir depoda yerləşir - çətir layihəsi. Eyni zamanda, mikroservislər maksimum təcrid olunur və ayrıca bir qurum üçün cavabdeh olan sadə əməliyyatları yerinə yetirir. Bu yanaşma ilə bütün sistemin API-sini saxlamaq asandır, dəyişikliklər etmək asandır, vahid və inteqrasiya testlərini yazmaq rahatdır.

Sistem komponentləri birbaşa və ya broker vasitəsilə qarşılıqlı əlaqədə olur. Mesajlaşma nöqteyi-nəzərindən hər bir xidmətin bir neçə həyat mərhələsi var:

  • Xidmətin işə salınması.
    Bu mərhələdə xidməti icra edən proses və asılılıqlar konfiqurasiya edilir və işə salınır.
  • Mübadilə nöqtəsinin yaradılması.
    Xidmət qovşaq konfiqurasiyasında göstərilən statik mübadilə nöqtəsindən istifadə edə və ya dinamik olaraq mübadilə nöqtələri yarada bilər.
  • Xidmətin qeydiyyatı.
    Xidmətin sorğulara xidmət göstərməsi üçün o, mübadilə məntəqəsində qeydiyyatdan keçməlidir.
  • Normal işləmə.
    Xidmət faydalı iş yaradır.
  • İşin tamamlanması.
    2 növ söndürmə mümkündür: normal və təcili. Normal iş zamanı xidmət mübadilə məntəqəsindən ayrılır və dayanır. Fövqəladə hallarda, mesajlaşma uğursuzluq skriptlərindən birini yerinə yetirir.

Bu olduqca mürəkkəb görünür, lakin kod o qədər də qorxulu deyil. Şərhlərlə kod nümunələri şablonların təhlilində bir az sonra veriləcək.

Mübadilə

Mübadilə nöqtəsi mesajlaşma şablonu daxilində komponentlərlə qarşılıqlı əlaqənin məntiqini həyata keçirən mesajlaşma prosesidir. Aşağıda təqdim olunan bütün nümunələrdə komponentlər mübadilə nöqtələri vasitəsilə qarşılıqlı əlaqədə olur, onların birləşməsi mesajlaşma təşkil edir.

Mesaj mübadiləsi nümunələri (APP)

Qlobal miqyasda mübadilə nümunələri ikitərəfli və birtərəfli bölünə bilər. Birincisi gələn mesaja cavabı nəzərdə tutur, ikincisi yox. Müştəri-server arxitekturasında ikitərəfli modelin klassik nümunəsi Sorğu-cavab nümunəsidir. Şablon və onun modifikasiyalarına baxaq.

Sorğu-cavab və ya RPC

RPC başqa bir prosesdən cavab almalı olduğumuz zaman istifadə olunur. Bu proses eyni qovşaqda işləyə və ya başqa qitədə yerləşə bilər. Aşağıda mesajlaşma vasitəsilə müştəri və server arasında qarşılıqlı əlaqənin diaqramı verilmişdir.

Paylanmış proqramların tikinti blokları. İlk yanaşma

Mesajlaşma tamamilə asinxron olduğundan, müştəri üçün mübadilə 2 mərhələyə bölünür:

  1. İstək göndər

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

    mübadilə ‒ mübadilə məntəqəsinin unikal adı
    ResponseMatchingTag ‒ cavabın işlənməsi üçün yerli etiket. Məsələn, müxtəlif istifadəçilərə məxsus bir neçə eyni sorğunun göndərilməsi halında.
    Request Definition - sorğu orqanı
    HandlerProcess ‒ İşləyicinin PID-si. Bu proses serverdən cavab alacaq.

  2. Cavab emal olunur

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

    ResponsePayload - server cavabı.

Server üçün proses də 2 mərhələdən ibarətdir:

  1. Mübadilə nöqtəsinin işə salınması
  2. Qəbul edilmiş sorğuların işlənməsi

Gəlin bu şablonu kodla təsvir edək. Deyək ki, biz tək dəqiq vaxt metodunu təmin edən sadə bir xidmət həyata keçirməliyik.

Server kodu

Xidmət API-ni api.hrl-də müəyyən edək:

%% =====================================================
%%  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-də xidmət nəzarətçisini təyin edək

%% В примере показан только значимый код. Вставив его в шаблон 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.

Müştəri kodu

Xidmətə sorğu göndərmək üçün müştərinin istənilən yerində mesajlaşma sorğusu API-yə zəng edə bilərsiniz:

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

Paylanmış sistemdə komponentlərin konfiqurasiyası çox fərqli ola bilər və sorğu zamanı mesajlaşma hələ başlamaya bilər və ya xidmət nəzarətçisi sorğuya xidmət göstərməyə hazır olmayacaq. Buna görə də, mesajlaşma cavabını yoxlamaq və uğursuzluq halını idarə etməliyik.
Uğurlu göndərildikdən sonra müştəri xidmətdən cavab və ya xəta alacaq.
Gəlin handle_info-da hər iki işi həll edək:

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

Sorğu-Böyük Cavab

Böyük mesajlar göndərməkdən çəkinmək yaxşıdır. Bütün sistemin həssaslığı və sabit işləməsi bundan asılıdır. Əgər sorğuya cavab çox yaddaş tutursa, onu hissələrə bölmək məcburidir.

Paylanmış proqramların tikinti blokları. İlk yanaşma

İcazə verin, belə hallara bir neçə misal verim:

  • Komponentlər fayllar kimi ikili məlumat mübadiləsi edir. Cavabın kiçik hissələrə bölünməsi sizə istənilən ölçülü fayllarla səmərəli işləməyə və yaddaş daşqınlarının qarşısını almağa kömək edir.
  • Siyahılar. Məsələn, verilənlər bazasındakı nəhəng cədvəldən bütün qeydləri seçib başqa komponentə köçürməliyik.

Mən bu cavabları lokomotiv adlandırıram. Hər halda, 1024 MB-lıq 1 mesaj 1 GB-lıq bir mesajdan daha yaxşıdır.

Erlang klasterində biz əlavə fayda əldə edirik - mübadilə nöqtəsi və şəbəkədəki yükü azaldır, çünki cavablar mübadilə nöqtəsini keçərək dərhal alıcıya göndərilir.

Sorğu ilə cavab

Bu, dialoq sistemlərinin qurulması üçün RPC modelinin olduqca nadir modifikasiyasıdır.

Paylanmış proqramların tikinti blokları. İlk yanaşma

Nəşr et-abunə ol (məlumat paylama ağacı)

Hadisəyə əsaslanan sistemlər məlumat hazır olan kimi onları istehlakçılara çatdırır. Beləliklə, sistemlər çəkmə və ya sorğu modelinə nisbətən təkan modelinə daha çox meyllidir. Bu xüsusiyyət, məlumatları daim tələb etmək və gözləməklə resurs israfının qarşısını almağa imkan verir.
Şəkildə müəyyən bir mövzuya abunə olan istehlakçılara mesajın yayılması prosesi göstərilir.

Paylanmış proqramların tikinti blokları. İlk yanaşma

Bu modeldən istifadənin klassik nümunələri dövlətin paylanmasıdır: kompüter oyunlarında oyun dünyası, birjalardakı bazar məlumatları, məlumat lentlərində faydalı məlumatlar.

Gəlin abunəçi koduna baxaq:

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.

Mənbə mesajı istənilən əlverişli yerdə dərc etmək üçün funksiyaya zəng edə bilər:

messaging:publish_message(Exchange, Key, Message).

mübadilə - mübadilə məntəqəsinin adı,
Aparıcı - marşrut açarı
Mesaj - faydalı yük

Ters çevrilmiş Nəşr et-abunə ol

Paylanmış proqramların tikinti blokları. İlk yanaşma

Pub-sub-u genişləndirməklə, giriş üçün əlverişli nümunə əldə edə bilərsiniz. Mənbələr və istehlakçılar dəsti tamamilə fərqli ola bilər. Şəkildə bir istehlakçı və birdən çox mənbə ilə iş göstərilir.

Tapşırıqların paylanması modeli

Demək olar ki, hər bir layihə hesabatların yaradılması, bildirişlərin çatdırılması və üçüncü tərəf sistemlərindən məlumatların alınması kimi təxirə salınmış emal tapşırıqlarını əhatə edir. Bu tapşırıqları yerinə yetirən sistemin ötürmə qabiliyyəti işləyicilər əlavə etməklə asanlıqla ölçülə bilər. Bizə qalan prosessorlar klasterini formalaşdırmaq və onlar arasında tapşırıqları bərabər paylamaqdır.

3 idarəçinin nümunəsindən istifadə edərək yaranan vəziyyətlərə baxaq. Tapşırıqların bölüşdürülməsi mərhələsində belə, bölüşdürülmənin ədalətliliyi və işləyicilərin daşması məsələsi ortaya çıxır. Dəyirmi sistemli paylama ədalətliliyə cavabdeh olacaq və idarəçilərin daşması vəziyyətindən qaçmaq üçün məhdudiyyət tətbiq edəcəyik. əvvəlcədən gətirmə_limiti. Keçici şəraitdə əvvəlcədən gətirmə_limiti bir işləyicinin bütün tapşırıqları almasına mane olacaq.

Mesajlaşma növbələri və emal prioritetini idarə edir. Prosessorlar gələn kimi tapşırıqlar alırlar. Tapşırıq uğurla və ya uğursuz ola bilər:

  • messaging:ack(Tack) - mesaj uğurla emal edildikdə çağırılır
  • messaging:nack(Tack) - bütün fövqəladə hallarda çağırılır. Tapşırıq qaytarıldıqdan sonra mesajlaşma onu başqa idarəçiyə ötürəcək.

Paylanmış proqramların tikinti blokları. İlk yanaşma

Tutaq ki, üç tapşırığın icrası zamanı mürəkkəb nasazlıq baş verdi: 1-ci prosessor tapşırığı aldıqdan sonra mübadilə nöqtəsinə heç nə bildirməyə vaxt tapmadan qəzaya uğradı. Bu halda, mübadilə nöqtəsi ack timeout müddəti bitdikdən sonra tapşırığı başqa işləyiciyə ötürəcək. Nədənsə, işləyici 3 tapşırığı tərk etdi və nack göndərdi; nəticədə, tapşırıq da onu uğurla tamamlayan başqa bir idarəçiyə verildi.

İlkin xülasə

Biz paylanmış sistemlərin əsas tikinti bloklarını əhatə etdik və onların Erlang/Elixir-də istifadəsi haqqında əsas anlayış əldə etdik.

Əsas nümunələri birləşdirərək, ortaya çıxan problemləri həll etmək üçün mürəkkəb paradiqmalar qura bilərsiniz.

Seriyanın yekun hissəsində xidmətlərin təşkili, marşrutlaşdırma və balanslaşdırmanın ümumi məsələlərinə baxacağıq, həmçinin sistemlərin miqyaslılığının və nasazlıqlara dözümlülüyün praktik tərəfi haqqında danışacağıq.

İkinci hissənin sonu.

foto Marius Kristensen
İllüstrasiyalar websequencediagrams.com istifadə edərək hazırlanmışdır

Mənbə: www.habr.com

Добавить комментарий