{"id":30908,"date":"2019-10-31T21:38:07","date_gmt":"2019-10-31T18:38:07","guid":{"rendered":"https:\/\/prohoster.info\/blog\/stroitelnye-bloki-raspredelennyh-prilozhenij-pervoe-priblizhenie\/"},"modified":"2019-10-31T21:38:07","modified_gmt":"2019-10-31T18:38:07","slug":"stroitelnye-bloki-raspredelennyh-prilozhenij-pervoe-priblizhenie","status":"publish","type":"post","link":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/stroitelnye-bloki-raspredelennyh-prilozhenij-pervoe-priblizhenie","title":{"rendered":"Bloki budowlane aplikacji rozproszonych. Pierwsze przybli\u017cenie","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Bloki budowlane aplikacji rozproszonych. Pierwsze przybli\u017cenie\" src=\"\/wp-content\/uploads\/2019\/04\/7ef485e452075775ce317b557671274c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>W poprzedniej <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/446028\/\">artyku\u0142<\/a><\/noindex> Om\u00f3wili\u015bmy teoretyczne podstawy architektury reaktywnej. Czas porozmawia\u0107 o strumieniach danych, sposobach realizacji system\u00f3w reaktywnych Erlang\/Elixir oraz wzorcach wymiany wiadomo\u015bci w nich:<\/p>\n<p><\/p>\n<ul>\n<li>\u017b\u0105danie-odpowied\u017a<\/li>\n<li>\u017b\u0105danie-odpowied\u017a dzielona<\/li>\n<li>Odpowied\u017a z \u017c\u0105daniem<\/li>\n<li>Pub-sub (publikowanie-subskrybowanie)<\/li>\n<li>Odwr\u00f3cone Pub-sub<\/li>\n<li>Rozdzielanie zada\u0144<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"soa-msa-i-obmen-soobscheniyami\">SOA, MSA i wymiana wiadomo\u015bci<\/h2>\n<p><\/p>\n<p>SOA, MSA to architektury systemowe, kt\u00f3re okre\u015blaj\u0105 zasady budowania system\u00f3w, podczas gdy messaging dostarcza prymitywy do ich realizacji.<\/p>\n<p><\/p>\n<p>Nie chc\u0119 propagowa\u0107 \u017cadnej architektury budowy system\u00f3w. Opowiadam si\u0119 za stosowaniem maksymalnie efektywnych i u\u017cytecznych praktyk dla konkretnego projektu i biznesu. Niezale\u017cnie od wybranej paradygmy, lepiej jest tworzy\u0107 bloki systemowe z uwzgl\u0119dnieniem podej\u015bcia Unix-way: komponenty o minimalnym powi\u0105zaniu, odpowiedzialne za pojedyncze encje. Metody API realizuj\u0105 maksymalnie proste dzia\u0142ania na encjach.<\/p>\n<p><\/p>\n<p>Messaging \u2012 jak to wynika z nazwy \u2012 to broker wiadomo\u015bci. Jego g\u0142\u00f3wnym celem jest przyjmowanie i wysy\u0142anie wiadomo\u015bci. Odpowiada za interfejsy wysy\u0142ania informacji, tworzenie logicznych kana\u0142\u00f3w przesy\u0142ania informacji wewn\u0105trz systemu, routowanie i balansowanie obci\u0105\u017cenia, a tak\u017ce przetwarzanie awarii na poziomie systemowym.<br \/>\nTworzony messaging nie pr\u00f3buje konkurowa\u0107 z rabbitmq ani go zast\u0119powa\u0107. Jego g\u0142\u00f3wne cechy:<\/p>\n<p><\/p>\n<ul>\n<li>Rozproszenie.<br \/>\nPunkty wymiany mo\u017cna tworzy\u0107 na wszystkich w\u0119z\u0142ach klastra, maksymalnie blisko do kodu, kt\u00f3ry je wykorzystuje.<\/li>\n<li>Prostota.<br \/>\nSkupienie na minimalizacji kodu szablonowego oraz \u0142atwo\u015bci u\u017cycia.<\/li>\n<li>Najlepsza wydajno\u015b\u0107.<br \/>\nNie pr\u00f3bujemy powt\u00f3rzy\u0107 funkcjonalno\u015bci rabbitmq, a jedynie oddzielamy warstw\u0119 architektoniczn\u0105 i transportow\u0105, kt\u00f3r\u0105 \u0142atwo integrowa\u0107 z OTP, minimalizuj\u0105c koszty.<\/li>\n<li>Elastyczno\u015b\u0107.<br \/>\nKa\u017cda us\u0142uga mo\u017ce \u0142\u0105czy\u0107 w sobie wiele wzorc\u00f3w wymiany.<\/li>\n<li>Odporno\u015b\u0107 na b\u0142\u0119dy zakorzeniona w projekcie.<\/li>\n<li>Skalowalno\u015b\u0107.<br \/>\nMessaging rozwija si\u0119 wraz z aplikacj\u0105. W miar\u0119 wzrostu obci\u0105\u017cenia mo\u017cna przenie\u015b\u0107 punkty wymiany na oddzielne maszyny.<\/li>\n<\/ul>\n<p><\/p>\n<p><em>Uwaga.<\/em> Z perspektywy organizacji kodu, meta-projekty s\u0105 doskona\u0142ym rozwi\u0105zaniem dla z\u0142o\u017conych system\u00f3w na Erlangu\/Elixiru. Ca\u0142y kod projektu znajduje si\u0119 w jednym repozytorium \u2012 projekcie nadrz\u0119dnym. Mikrous\u0142ugi s\u0105 maksymalnie izolowane i wykonuj\u0105 proste operacje, odpowiadaj\u0105ce za poszczeg\u00f3lne jednostki. Takie podej\u015bcie u\u0142atwia zarz\u0105dzanie API ca\u0142ego systemu, wprowadzanie zmian oraz pisanie test\u00f3w jednostkowych i integracyjnych.<\/p>\n<p><\/p>\n<p>Komponenty systemu komunikuj\u0105 si\u0119 bezpo\u015brednio lub za po\u015brednictwem brokera. Z perspektywy komunikacji, ka\u017cda us\u0142uga ma kilka faz \u017cycia:<\/p>\n<p><\/p>\n<ul>\n<li>Inicjalizacja us\u0142ugi.<br \/>\nNa tym etapie odbywa si\u0119 konfiguracja i uruchomienie procesu us\u0142ugowego oraz zale\u017cno\u015bci.<\/li>\n<li>Tworzenie punktu wymiany.<br \/>\nUs\u0142uga mo\u017ce korzysta\u0107 z statycznego punktu wymiany, okre\u015blonego w konfiguracji w\u0119z\u0142a, lub tworzy\u0107 punkty wymiany dynamicznie. <\/li>\n<li>Rejestracja us\u0142ugi.<br \/>\nAby us\u0142uga mog\u0142a obs\u0142ugiwa\u0107 \u017c\u0105dania, nale\u017cy j\u0105 zarejestrowa\u0107 w punkcie wymiany.<\/li>\n<li>Normalne funkcjonowanie.<br \/>\nUs\u0142uga wykonuje po\u017cyteczn\u0105 prac\u0119.<\/li>\n<li>Zako\u0144czenie pracy.<br \/>\nS\u0105 dwa rodzaje zako\u0144czenia pracy: normalne i awaryjne. W przypadku normalnego, us\u0142uga jest od\u0142\u0105czana od punktu wymiany i zatrzymywana. W sytuacjach awaryjnych komunikacja realizuje jeden z scenariuszy obs\u0142ugi awarii.<\/li>\n<\/ul>\n<p><\/p>\n<p>Wygl\u0105da na do\u015b\u0107 skomplikowane, ale w kodzie nie jest tak strasznie. Przyk\u0142ady kodu z komentarzami b\u0119d\u0105 przedstawione w analizie szablon\u00f3w nieco p\u00f3\u017aniej.<\/p>\n<p><\/p>\n<h2 id=\"exchanges\">Wymiany<\/h2>\n<p><\/p>\n<p>Punkt wymiany \u2012 proces komunikacji, realizuj\u0105cy logik\u0119 interakcji z komponentami w ramach szablonu wymiany wiadomo\u015bci. We wszystkich przyk\u0142adach poni\u017cej komponenty komunikuj\u0105 si\u0119 poprzez punkty wymiany, kt\u00f3rych kombinacja tworzy komunikacj\u0119.<\/p>\n<p><\/p>\n<h2 id=\"message-exchange-patterns-meps\">Wzory wymiany wiadomo\u015bci (MEP)<\/h2>\n<p><\/p>\n<p>Globalnie, wzory wymiany mo\u017cna podzieli\u0107 na dwustronne i jednostronne. Pierwsze zak\u0142adaj\u0105 odpowied\u017a na przychodz\u0105c\u0105 wiadomo\u015b\u0107, drugie tego nie robi\u0105. Klasycznym przyk\u0142adem dwustronnego wzoru w architekturze klient-serwer jest wz\u00f3r Request-response. Przyjrzyjmy si\u0119 wzorowi oraz jego modyfikacjom.<\/p>\n<p><\/p>\n<h3 id=\"requestresponse-ili-rpc\">Request\u2013response lub RPC<\/h3>\n<p><\/p>\n<p>RPC jest u\u017cywane, gdy potrzebujemy uzyska\u0107 odpowied\u017a od innego procesu. Ten proces mo\u017ce by\u0107 uruchomiony na tym samym w\u0119\u017ale lub znajdowa\u0107 si\u0119 na innym kontynencie. Poni\u017cej przedstawiona jest schemat interakcji klienta i <a class=\"wpil_keyword_link\" href=\"https:\/\/prohoster.info\/pl\/server\/dts-los-angeles\/\"   title=\"serwera\" data-wpil-keyword-link=\"linked\"  data-wpil-monitor-id=\"3483\">serwera<\/a> poprzez komunikacj\u0119.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Bloki budowlane aplikacji rozproszonych. Pierwsze przybli\u017cenie\" src=\"\/wp-content\/uploads\/2019\/04\/091725fc108d6ce1a5fc9ae866e2c395.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Poniewa\u017c komunikacja jest ca\u0142kowicie asynchroniczna, wymiana dla klienta dzieli si\u0119 na 2 fazy:<\/p>\n<p><\/p>\n<ol>\n<li>\n<p>Wysy\u0142anie \u017c\u0105dania<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">messaging:request(Exchange, ResponseMatchingTag, RequestDefinition, HandlerProcess).<\/code><\/pre>\n<p><\/p>\n<p><em>Exchange<\/em> \u2012 unikalna nazwa punktu wymiany<br \/>\n<em>ResponseMatchingTag<\/em> \u2012 lokalna etykieta do przetwarzania odpowiedzi. Na przyk\u0142ad w przypadku wysy\u0142ania wielu identycznych \u017c\u0105da\u0144 od r\u00f3\u017cnych u\u017cytkownik\u00f3w.<br \/>\n<em>RequestDefinition<\/em> \u2012 tre\u015b\u0107 \u017c\u0105dania<br \/>\n<em>HandlerProcess<\/em> \u2012 PID przetwarzacza. Ten proces otrzyma odpowied\u017a od serwera.<\/p>\n<p>\n<\/li>\n<li>\n<p>Przetwarzanie odpowiedzi<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">handle_info(#'$msg'{exchange = EXCHANGE, tag = ResponseMatchingTag,message = ResponsePayload}, State)<\/code><\/pre>\n<p><\/p>\n<p><em>ResponsePayload<\/em> \u2012 odpowied\u017a serwera.<\/p>\n<p>\n<\/li>\n<\/ol>\n<p><\/p>\n<p>Dla serwera proces sk\u0142ada si\u0119 z 2 faz:<\/p>\n<p><\/p>\n<ol>\n<li>Inicjalizacja punktu wymiany<\/li>\n<li>Przetwarzanie przychodz\u0105cych \u017c\u0105da\u0144<\/li>\n<\/ol>\n<p><\/p>\n<p>Ilustrujemy ten szablon kodem. Za\u0142\u00f3\u017cmy, \u017ce musimy zrealizowa\u0107 prost\u0105 us\u0142ug\u0119, kt\u00f3ra udost\u0119pnia jedn\u0105 metod\u0119 zwracaj\u0105c\u0105 dok\u0142adny czas.<\/p>\n<p><\/p>\n<h4 id=\"kod-servera\">Kod serwera<\/h4>\n<p><\/p>\n<p>Wyodr\u0119bnijmy definicj\u0119 API us\u0142ugi do api.hrl:<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">%% =====================================================\n%%  encje\n%% =====================================================\n-record(time, {\n  unixtime :: non_neg_integer(),\n  datetime :: binary()\n}).\n\n-record(time_error, {\n  code :: non_neg_integer(),\n  error :: term()\n}).\n\n%% =====================================================\n%%  metody\n%% =====================================================\n-record(time_req, {\n  opts :: term()\n}).\n-record(time_resp, {\n  result :: #time{} | #time_error{}\n}).<\/code><\/pre>\n<p><\/p>\n<p>Zdefiniujmy kontroler us\u0142ugi w time_controller.erl<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">%% W przyk\u0142adzie pokazany jest tylko warto\u015bciowy kod. Wstawiaj\u0105c go do szablonu gen_server, mo\u017cna uzyska\u0107 dzia\u0142aj\u0105c\u0105 us\u0142ug\u0119.\n\n%% inicjalizacja gen_server\ninit(Args) -&gt;\n  %% po\u0142\u0105czenie z punktem wymiany\n  messaging:monitor_exchange(req_resp, ?EXCHANGE, default, self())\n  {ok, #{}}.\n\n%% obs\u0142uga zdarzenia utraty po\u0142\u0105czenia z punktem wymiany. To samo zdarzenie pojawia si\u0119, je\u015bli punkt wymiany jeszcze nie zosta\u0142 uruchomiony.\nhandle_info(#exchange_die{exchange = ?EXCHANGE}, State) -&gt;\n  erlang:send(self(), monitor_exchange),\n  {noreply, State};\n\n%% obs\u0142uga API\nhandle_info(#time_req{opts = _Opts}, State) -&gt;\n  messaging:response_once(Client, #time_resp{\nresult = #time{ unixtime = time_utils:unixtime(now()), datetime = time_utils:iso8601_fmt(now())}\n  });\n  {noreply, State};\n\n%% zako\u0144czenie pracy gen_server\nterminate(_Reason, _State) -&gt;\n  messaging:demonitor_exchange(req_resp, ?EXCHANGE, default, self()),\n  ok.<\/code><\/pre>\n<p><\/p>\n<h4 id=\"kod-klienta\">Kod klienta<\/h4>\n<p><\/p>\n<p>Aby wys\u0142a\u0107 \u017c\u0105danie do us\u0142ugi, w dowolnym miejscu klienta mo\u017cna wywo\u0142a\u0107 interfejs API messaging request:<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">case messaging:request(?EXCHANGE, tag, #time_req{opts = #{}}, self()) of\n    ok -&gt; ok;\n    _ -&gt; %% logika powt\u00f3rzenia lub b\u0142\u0119du\nend<\/code><\/pre>\n<p><\/p>\n<p>W rozproszonym systemie konfiguracja komponent\u00f3w mo\u017ce by\u0107 bardzo r\u00f3\u017cna, a w momencie \u017c\u0105dania messaging mo\u017ce jeszcze nie by\u0107 uruchomiony, albo kontroler us\u0142ugi nie jest gotowy, aby obs\u0142u\u017cy\u0107 \u017c\u0105danie. Dlatego musimy sprawdzi\u0107 odpowied\u017a messaging i obs\u0142u\u017cy\u0107 przypadek odmowy.<br \/>\nPo pomy\u015blnym wys\u0142aniu klient otrzyma odpowied\u017a lub b\u0142\u0105d od us\u0142ugi.<br \/>\nObs\u0142u\u017cmy oba przypadki w handle_info:<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">handle_info(#'$msg'{exchange = ?EXCHANGE, tag = tag, message = #time_resp{result = #time{unixtime = Utime}}}, State) -&gt;\n  ?debugVal(Utime),\n  {noreply, State};\n\nhandle_info(#'$msg'{exchange = ?EXCHANGE, tag = tag, message = #time_resp{result = #time_error{code = ErrorCode}}}, State) -&gt;\n  ?debugVal({error, ErrorCode}),\n  {noreply, State};<\/code><\/pre>\n<p><\/p>\n<h3 id=\"request-chunked-response\">\u017b\u0105danie-odpowied\u017a dzielona<\/h3>\n<p><\/p>\n<p>Lepiej unika\u0107 przesy\u0142ania du\u017cych wiadomo\u015bci. Zale\u017cy od tego responsywno\u015b\u0107 i stabilno\u015b\u0107 ca\u0142ego systemu. Je\u015bli odpowied\u017a na zapytanie zajmuje du\u017co pami\u0119ci, podzia\u0142 na cz\u0119\u015bci jest obowi\u0105zkowy.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Bloki budowlane aplikacji rozproszonych. Pierwsze przybli\u017cenie\" src=\"\/wp-content\/uploads\/2019\/04\/bd60742a933a8e4e3eeaf7f4adf00f2a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Podam kilka przyk\u0142ad\u00f3w takich przypadk\u00f3w:<\/p>\n<p><\/p>\n<ul>\n<li>Komponenty wymieniaj\u0105 si\u0119 danymi binarnymi, na przyk\u0142ad plikami. Podzia\u0142 odpowiedzi na ma\u0142e cz\u0119\u015bci umo\u017cliwia efektywne przetwarzanie plik\u00f3w o dowolnym rozmiarze i unikanie przepe\u0142nienia pami\u0119ci.<\/li>\n<li>Listy. Na przyk\u0142ad, musimy wybra\u0107 wszystkie zapisy z ogromnej tabeli w bazie danych i przekaza\u0107 je innemu komponentowi.<\/li>\n<\/ul>\n<p><\/p>\n<p>Nazywam takie odpowiedzi lokomotyw\u0105. W ka\u017cdym razie, 1024 wiadomo\u015bci po 1 MB jest lepsze ni\u017c jedna wiadomo\u015b\u0107 o wielko\u015bci 1 GB. <\/p>\n<p><\/p>\n<p>W klastrze Erlang uzyskujemy dodatkowy zysk \u2012 zmniejszenie obci\u0105\u017cenia punktu wymiany i sieci, poniewa\u017c odpowiedzi s\u0105 od razu kierowane do odbiorcy, omijaj\u0105c punkt wymiany.<\/p>\n<p><\/p>\n<h3 id=\"response-with-request\">Odpowied\u017a z \u017c\u0105daniem<\/h3>\n<p><\/p>\n<p>Jest to do\u015b\u0107 rzadkie modyfikowanie wzorca RPC do budowania system\u00f3w dialogowych.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Bloki budowlane aplikacji rozproszonych. Pierwsze przybli\u017cenie\" src=\"\/wp-content\/uploads\/2019\/04\/9b350a0b48ca8acdca973da2f7eadb76.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<h3 id=\"publish-subscribe-data-distribution-tree\">Publish-subscribe (drzewo dystrybucji danych)<\/h3>\n<p><\/p>\n<p>Systemy zorientowane na zdarzenia dostarczaj\u0105 dane konsumentom w miar\u0119 ich gotowo\u015bci. W ten spos\u00f3b systemy s\u0105 bardziej sk\u0142onne do modelu push ni\u017c pull czy poll. Ta cecha pozwala nie marnowa\u0107 zasob\u00f3w na ci\u0105g\u0142e pytanie i oczekiwanie na dane.<br \/>\nNa ilustracji przedstawiono proces rozprzestrzeniania wiadomo\u015bci do konsument\u00f3w, kt\u00f3rzy s\u0105 subskrybowani na dany temat.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Bloki budowlane aplikacji rozproszonych. Pierwsze przybli\u017cenie\" src=\"\/wp-content\/uploads\/2019\/04\/e211d5dcfeb5ce8513699eac8e3b6d84.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Klasycznymi przyk\u0142adami u\u017cycia tego wzorca jest dystrybucja stanu: \u015bwiata gry w grach komputerowych, danych rynkowych na gie\u0142dach, u\u017cytecznych informacji w zasilaczach danych.<\/p>\n<p><\/p>\n<p>Rozwa\u017cmy kod subskrybenta:<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">init(_Args) -&gt;\n  %% subskrybujemy si\u0119 na punkt wymiany, klucz = key\n  messaging:subscribe(?SUBSCRIPTION, key, tag, self()),\n  {ok, #{}}.\n\nhandle_info(#exchange_die{exchange = ?SUBSCRIPTION}, State) -&gt;\n  %% je\u015bli punkt wymiany jest niedost\u0119pny, pr\u00f3bujemy ponownie si\u0119 po\u0142\u0105czy\u0107\n  messaging:subscribe(?SUBSCRIPTION, key, tag, self()),\n  {noreply, State};\n\n%% przetwarzamy nadchodz\u0105ce wiadomo\u015bci\nhandle_info(#'$msg'{exchange = ?SUBSCRIPTION, message = Msg}, State) -&gt;\n  ?debugVal(Msg),\n  {noreply, State};\n\n%% przy zatrzymaniu konsumenta - roz\u0142\u0105czamy si\u0119 od punktu wymiany\nterminate(_Reason, _State) -&gt;\n  messaging:unsubscribe(?SUBSCRIPTION, key, tag, self()),\n  ok.<\/code><\/pre>\n<p><\/p>\n<p>\u0179r\u00f3d\u0142o mo\u017ce wywo\u0142a\u0107 funkcj\u0119 publikacji wiadomo\u015bci w dowolnym miejscu:<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">messaging:publish_message(Exchange, Key, Message).<\/code><\/pre>\n<p><\/p>\n<p><em>Exchange<\/em> \u2012 nazwa punktu wymiany,<br \/>\n<em>Klucz<\/em> \u2012 klucz routingu<br \/>\n<em>Wiadomo\u015b\u0107<\/em> \u2012 \u0142adunek u\u017cyteczny<\/p>\n<p><\/p>\n<h2 id=\"inverted-publish-subscribe\">Odwr\u00f3cone Pub-sub<\/h2>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Bloki budowlane aplikacji rozproszonych. Pierwsze przybli\u017cenie\" src=\"\/wp-content\/uploads\/2019\/04\/2ef7cd7de459455937bdcfa8a11fd028.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Rozwijaj\u0105c pub-sub, mo\u017cna uzyska\u0107 wz\u00f3r odpowiedni do logowania. Zestaw \u017ar\u00f3de\u0142 i konsument\u00f3w mo\u017ce by\u0107 zupe\u0142nie r\u00f3\u017cny. Na rysunku przedstawiono przypadek z jednym konsumentem i wieloma \u017ar\u00f3d\u0142ami.<\/p>\n<p><\/p>\n<h2 id=\"task-distribution-pattern\">Wz\u00f3r dystrybucji zada\u0144<\/h2>\n<p><\/p>\n<p>W prawie ka\u017cdym projekcie wyst\u0119puj\u0105 zadania o odroczonym przetwarzaniu, takie jak generowanie raport\u00f3w, dostarczanie powiadomie\u0144, uzyskiwanie danych z zewn\u0119trznych system\u00f3w. Przepustowo\u015b\u0107 systemu wykonuj\u0105cego te zadania \u0142atwo skalowa\u0107 poprzez dodanie przetw\u00f3rc\u00f3w. Wszystko, co nam pozostaje, to utworzy\u0107 klaster przetw\u00f3rc\u00f3w i r\u00f3wnomiernie rozdziela\u0107 zadania mi\u0119dzy nimi.<\/p>\n<p><\/p>\n<p>Rozwa\u017cmy sytuacje, kt\u00f3re mog\u0105 wyst\u0105pi\u0107 na przyk\u0142adzie trzech przetw\u00f3rc\u00f3w. Ju\u017c na etapie rozdzielania zada\u0144 pojawia si\u0119 kwestia sprawiedliwo\u015bci dystrybucji oraz przepe\u0142nienia przetw\u00f3rc\u00f3w. Za sprawiedliwo\u015b\u0107 odpowiada dystrybucja round-robin, a aby unikn\u0105\u0107 sytuacji przepe\u0142nienia przetw\u00f3rc\u00f3w, wprowadzimy ograniczenie <em>prefetch_limit<\/em>. W trybach przej\u015bciowych <em>prefetch_limit<\/em> nie pozwoli jednemu przetw\u00f3rcy na otrzymanie wszystkich zada\u0144.<\/p>\n<p><\/p>\n<p>Messaging zarz\u0105dza kolejkami i priorytetami przetwarzania. Przetw\u00f3rcy otrzymuj\u0105 zadania w miar\u0119 ich przybywania. Wykonanie zadania mo\u017ce zako\u0144czy\u0107 si\u0119 sukcesem lub niepowodzeniem:<\/p>\n<p><\/p>\n<ul>\n<li><code>messaging:ack(Tack)<\/code> \u2012 wywo\u0142ywane w przypadku pomy\u015blnego przetworzenia wiadomo\u015bci<\/li>\n<li><code>messaging:nack(Tack)<\/code> \u2012 wywo\u0142ywane w nieprzewidzianych sytuacjach. Po zwr\u00f3ceniu zadania messaging przeka\u017ce je innemu przetw\u00f3rcy.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Bloki budowlane aplikacji rozproszonych. Pierwsze przybli\u017cenie\" src=\"\/wp-content\/uploads\/2019\/04\/c2c632b6411247ee8b0dcfb3250a4503.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Przypu\u015b\u0107my, \u017ce podczas przetwarzania trzech zada\u0144 wyst\u0105pi\u0142a powa\u017cna awaria: przetw\u00f3rca 1 po otrzymaniu zadania upad\u0142, nie zd\u0105\u017cywszy przekaza\u0107 nic punktowi wymiany. W takim przypadku punkt wymiany, po up\u0142ywie czasu na ack, przeka\u017ce zadanie innemu przetw\u00f3rcy. Przetw\u00f3rca 3 z jakiego\u015b powodu odm\u00f3wi\u0142 przyj\u0119cia zadania i wys\u0142a\u0142 nack, w efekcie zadanie r\u00f3wnie\u017c trafi\u0142o do innego przetw\u00f3rcy, kt\u00f3ry je pomy\u015blnie wykona\u0142.<\/p>\n<p><\/p>\n<h2 id=\"predvaritelnyy-itog\">Wst\u0119pny podsumowanie<\/h2>\n<p><\/p>\n<p>Om\u00f3wili\u015bmy podstawowe elementy system\u00f3w rozproszonych i uzyskali\u015bmy podstawowe zrozumienie ich zastosowania w Erlang'u\/Elixirze.<\/p>\n<p><\/p>\n<p>\u0141\u0105cz\u0105c podstawowe wzory, mo\u017cna budowa\u0107 z\u0142o\u017cone paradygmaty do rozwi\u0105zywania pojawiaj\u0105cych si\u0119 zada\u0144.<\/p>\n<p><\/p>\n<p>W ostatniej cz\u0119\u015bci cyklu om\u00f3wimy og\u00f3lne zagadnienia dotycz\u0105ce organizacji us\u0142ug, routingu i balansowania, a tak\u017ce poruszymy praktyczne aspekty skalowalno\u015bci i odporno\u015bci system\u00f3w na awarie.<\/p>\n<p><\/p>\n<p>Koniec drugiej cz\u0119\u015bci.<\/p>\n<p><\/p>\n<p>Zdj\u0119cie <noindex>Marius Christensen<\/noindex><br \/>\nIlustracje przygotowano za pomoc\u0105 websequencediagrams.com<\/p>\n<p>\u0179r\u00f3d\u0142o: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/446108\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412 \u043f\u0440\u043e\u0448\u043b\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438 \u0442\u0435\u043e\u0440\u0435\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u0441\u043d\u043e\u0432\u044b \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u043e\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b. \u041f\u0440\u0438\u0448\u043b\u043e \u0432\u0440\u0435\u043c\u044f \u043f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043e \u043f\u043e\u0442\u043e\u043a\u0430\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u043f\u0443\u0442\u044f\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0435\u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 Erlang\/Elixir \u0441\u0438\u0441\u0442\u0435\u043c \u0438 \u0448\u0430\u0431\u043b\u043e\u043d\u0430\u0445 \u043e\u0431\u043c\u0435\u043d\u0430 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 \u0432 \u043d\u0438\u0445: Request-response Request-Chunked Response Response with Request Publish-subscribe Inverted Publish-subscribe Task distribution SOA, MSA \u0438 \u043e\u0431\u043c\u0435\u043d \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f\u043c\u0438 SOA, MSA \u2013 \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u044b\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0438\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0430 \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u0441\u0438\u0441\u0442\u0435\u043c, \u0432 \u0442\u043e \u0432\u0440\u0435\u043c\u044f \u043a\u0430\u043a [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":22886,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-30908","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/stroitelnye-bloki-raspredelennyh-prilozhenij-pervoe-priblizhenie\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0421\u0442\u0440\u043e\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u041f\u0435\u0440\u0432\u043e\u0435 \u043f\u0440\u0438\u0431\u043b\u0438\u0436\u0435\u043d\u0438\u0435 | ProHoster\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/stroitelnye-bloki-raspredelennyh-prilozhenij-pervoe-priblizhenie\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T18:38:07+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T18:38:07+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Fundamenty aplikacji rozproszonych. Pierwsze przybli\u017cenie | ProHoster","description":"","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/stroitelnye-bloki-raspredelennyh-prilozhenij-pervoe-priblizhenie","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0421\u0442\u0440\u043e\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0431\u043b\u043e\u043a\u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0439. \u041f\u0435\u0440\u0432\u043e\u0435 \u043f\u0440\u0438\u0431\u043b\u0438\u0436\u0435\u043d\u0438\u0435 | ProHoster","og:url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/stroitelnye-bloki-raspredelennyh-prilozhenij-pervoe-priblizhenie","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T18:38:07+00:00","article:modified_time":"2019-10-31T18:38:07+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"30908","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-02-22 15:33:36","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 03:27:06","updated":"2026-02-22 15:33:36","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/30908","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=30908"}],"version-history":[{"count":1,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/30908\/revisions"}],"predecessor-version":[{"id":162009,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/30908\/revisions\/162009"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media\/22886"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=30908"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=30908"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=30908"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}