{"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\/en\/blog\/administrirovanie\/stroitelnye-bloki-raspredelennyh-prilozhenij-pervoe-priblizhenie","title":{"rendered":"Building blocks of distributed applications. The first approximation.","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Building blocks of distributed applications. The first approximation.\" src=\"\/wp-content\/uploads\/2019\/04\/7ef485e452075775ce317b557671274c.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Previously <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/446028\/\">article<\/a><\/noindex> We have covered the theoretical foundations of reactive architecture. It is time to discuss data streams, the implementation paths of reactive Erlang\/Elixir systems, and the messaging patterns within them:<\/p>\n<p><\/p>\n<ul>\n<li>Request-response<\/li>\n<li>Request-Chunked Response<\/li>\n<li>Response with Request<\/li>\n<li>Publish-subscribe<\/li>\n<li>Inverted Publish-subscribe<\/li>\n<li>Task distribution<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/li>\n<\/ul>\n<p><\/p>\n<h2 id=\"soa-msa-i-obmen-soobscheniyami\">SOA, MSA and messaging<\/h2>\n<p><\/p>\n<p>SOA and MSA are system architectures that define the rules for building systems, while messaging provides the primitives for their implementation.<\/p>\n<p><\/p>\n<p>I do not wish to promote any specific system architecture. I advocate for the application of the most effective and useful practices tailored for a particular project and business. Regardless of the paradigm we choose, it is better to create system components with a focus on the Unix way: components with minimal coupling that are responsible for distinct entities. API methods perform the simplest possible actions with these entities.<\/p>\n<p><\/p>\n<p>Messaging\u2014as the name suggests\u2014is a message broker. Its primary goal is to receive and deliver messages. It manages the interfaces for sending information, forming logical communication channels within the system, routing and load balancing, as well as handling failures at the system level.<br \/>\nThe messaging being developed does not aim to compete with rabbitmq or replace it. Its main features are:<\/p>\n<p><\/p>\n<ul>\n<li>Distribution.<br \/>\nExchange points can be created on all nodes of the cluster, as close to the code that uses them as possible.<\/li>\n<li>Simplicity.<br \/>\nFocus on minimizing boilerplate code and enhancing usability.<\/li>\n<li>Better performance.<br \/>\nWe do not attempt to replicate rabbitmq's functionality; instead, we isolate only the architectural and transport layer, integrating it as simply as possible into OTP to minimize overhead.<\/li>\n<li>Flexibility.<br \/>\nEach service can combine multiple messaging patterns.<\/li>\n<li>Fault tolerance built into the design.<\/li>\n<li>Scalability.<br \/>\nMessaging evolves alongside the application. As the load increases, messaging points can be moved to separate machines.<\/li>\n<\/ul>\n<p><\/p>\n<p><em>Note.<\/em> From a code organization perspective, meta-projects are well suited for complex systems built on Erlang\/Elixir. All project code resides in a single repository \u2013 an umbrella project. In this setup, microservices are maximally isolated and perform simple operations, each responsible for a distinct entity. This approach facilitates easy API maintenance across the entire system, makes modifications straightforward, and simplifies the writing of unit and integration tests.<\/p>\n<p><\/p>\n<p>The system components interact directly or through a broker. From a messaging standpoint, each service has several life phases:<\/p>\n<p><\/p>\n<ul>\n<li>Service initialization.<br \/>\nAt this stage, the configuration and launch of the executing service process and its dependencies occur.<\/li>\n<li>Creating an exchange.<br \/>\nThe service can either use a static exchange point defined in the node's configuration or create exchanges dynamically. <\/li>\n<li>Service registration.<br \/>\nTo handle requests, the service must be registered at the exchange point.<\/li>\n<li>Normal operation.<br \/>\nThe service performs useful work.<\/li>\n<li>Termination.<br \/>\nThere are two types of termination: graceful and abrupt. In graceful termination, the service disconnects from the exchange and stops. In case of emergencies, the messaging system follows one of the failure-handling scenarios.<\/li>\n<\/ul>\n<p><\/p>\n<p>It looks quite complex, but the code is not that daunting. Code examples with comments will be provided in the templates analysis shortly.<\/p>\n<p><\/p>\n<h2 id=\"exchanges\">Exchanges<\/h2>\n<p><\/p>\n<p>An exchange is a messaging process that implements the interaction logic with components within the messaging pattern. In all the examples presented below, components interact through exchanges, the combination of which forms the messaging system.<\/p>\n<p><\/p>\n<h2 id=\"message-exchange-patterns-meps\">Message exchange patterns (MEPs)<\/h2>\n<p><\/p>\n<p>Globally, exchange patterns can be divided into bidirectional and unidirectional. The former involves a response to the incoming message, while the latter does not. A classic example of a bidirectional pattern in a client-server architecture is the Request-response pattern. Let's examine this pattern and its variants.<\/p>\n<p><\/p>\n<h3 id=\"requestresponse-ili-rpc\">Request\u2013response or RPC<\/h3>\n<p><\/p>\n<p>RPC is used when we need to get a response from another process. This process can be running on the same node or located on another continent. Below is a diagram illustrating the interaction between the client and <a class=\"wpil_keyword_link\" href=\"https:\/\/prohoster.info\/en\/server\/dts-los-angeles\/\"   title=\"server\" data-wpil-keyword-link=\"linked\"  data-wpil-monitor-id=\"3483\">server<\/a> through messaging.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Building blocks of distributed applications. The first approximation.\" src=\"\/wp-content\/uploads\/2019\/04\/091725fc108d6ce1a5fc9ae866e2c395.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Since messaging is fully asynchronous, the exchange for the client is divided into two phases:<\/p>\n<p><\/p>\n<ol>\n<li>\n<p>Sending a request<\/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 a unique name for the exchange point<br \/>\n<em>ResponseMatchingTag<\/em> \u2012 a local tag for handling the response. For example, when sending multiple identical requests belonging to different users.<br \/>\n<em>RequestDefinition<\/em> \u2012 the body of the request<br \/>\n<em>HandlerProcess<\/em> \u2012 the handler's PID. This process will receive a response from the server.<\/p>\n<p>\n<\/li>\n<li>\n<p>Processing the response<\/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 server response.<\/p>\n<p>\n<\/li>\n<\/ol>\n<p><\/p>\n<p>For the server, the process also consists of 2 phases:<\/p>\n<p><\/p>\n<ol>\n<li>Initialization of the exchange point<\/li>\n<li>Processing incoming requests<\/li>\n<\/ol>\n<p><\/p>\n<p>Let's illustrate this template with code. Suppose we need to implement a simple service that provides a single method for getting the exact time.<\/p>\n<p><\/p>\n<h4 id=\"kod-servera\">Server code<\/h4>\n<p><\/p>\n<p>Let's move the service API definition to api.hrl:<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">%% =====================================================\n%%  entities\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%%  methods\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>Define the service controller in time_controller.erl<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">%% The example shows only the significant code. By inserting it into the gen_server template, a working service can be obtained.\n\n%% gen_server initialization\ninit(Args) -&gt;\n  %% connection to the exchange point\n  messaging:monitor_exchange(req_resp, ?EXCHANGE, default, self())\n  {ok, #{}}.\n\n%% handling the disconnection event from the exchange point. This event also comes if the exchange point hasn't started yet.\nhandle_info(#exchange_die{exchange = ?EXCHANGE}, State) -&gt;\n  erlang:send(self(), monitor_exchange),\n  {noreply, State};\n\n%% handling the 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%% terminating the 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\">Client code<\/h4>\n<p><\/p>\n<p>To send a request to the service, anywhere in the client you can call the messaging request API:<\/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; %% repeat or fail logic\nend<\/code><\/pre>\n<p><\/p>\n<p>In a distributed system, the configuration of components can vary greatly, and at the time of the request, the messaging may not yet be running, or the service controller may not be ready to handle the request. Therefore, we need to check the response from messaging and handle the failure case.<br \/>\nAfter a successful send, a response or error will come back to the client from the service.<br \/>\nWe will handle both cases in 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\">Request-Chunked Response<\/h3>\n<p><\/p>\n<p>It's better to avoid sending huge messages. This affects the responsiveness and stability of the entire system. If the response to a request takes up a lot of memory, splitting it into parts is mandatory.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Building blocks of distributed applications. The first approximation.\" src=\"\/wp-content\/uploads\/2019\/04\/bd60742a933a8e4e3eeaf7f4adf00f2a.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Here are a couple of examples of such cases:<\/p>\n<p><\/p>\n<ul>\n<li>Components exchange binary data, such as files. Breaking down the response into smaller parts helps efficiently work with files of any size and prevents memory overflow.<\/li>\n<li>Listings. For example, we need to select all records from a large table in the database and send them to another component.<\/li>\n<\/ul>\n<p><\/p>\n<p>I call such responses a train. In any case, 1024 messages of 1 MB are better than a single message of 1 GB. <\/p>\n<p><\/p>\n<p>In an Erlang cluster, we gain additional benefits\u2014reducing the load on the exchange point and the network, as responses are immediately directed to the recipient, bypassing the exchange point.<\/p>\n<p><\/p>\n<h3 id=\"response-with-request\">Response with Request<\/h3>\n<p><\/p>\n<p>This is a fairly rare modification of the RPC pattern for building dialog systems.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Building blocks of distributed applications. The first approximation.\" 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 (data distribution tree)<\/h3>\n<p><\/p>\n<p>Event-driven systems deliver data to consumers as it becomes available. Thus, the systems are more inclined toward a push model rather than pull or poll. This feature prevents wasting resources by constantly querying and waiting for data.<br \/>\nThe diagram shows the process of distributing messages to consumers subscribed to a particular topic.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Building blocks of distributed applications. The first approximation.\" src=\"\/wp-content\/uploads\/2019\/04\/e211d5dcfeb5ce8513699eac8e3b6d84.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Classic examples of using this pattern include the distribution of state: the game world in computer games, market data on exchanges, useful information in data feeds.<\/p>\n<p><\/p>\n<p>Let's look at the subscriber code:<\/p>\n<p><\/p>\n<pre><code class=\"erlang\">init(_Args) -&gt;\n  %% subscribe to the exchange, key = key\n  messaging:subscribe(?SUBSCRIPTION, key, tag, self()),\n  {ok, #{}}.\n\nhandle_info(#exchange_die{exchange = ?SUBSCRIPTION}, State) -&gt;\n  %% if the exchange point is unavailable, we try to reconnect\n  messaging:subscribe(?SUBSCRIPTION, key, tag, self()),\n  {noreply, State};\n\n%% handle incoming messages\nhandle_info(#'$msg'{exchange = ?SUBSCRIPTION, message = Msg}, State) -&gt;\n  ?debugVal(Msg),\n  {noreply, State};\n\n%% when the consumer stops - unsubscribe from the exchange\nterminate(_Reason, _State) -&gt;\n  messaging:unsubscribe(?SUBSCRIPTION, key, tag, self()),\n  ok.<\/code><\/pre>\n<p><\/p>\n<p>The source can trigger the message publishing function at any convenient location:<\/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 exchange point name,<br \/>\n<em>Key<\/em> \u2012 routing key<br \/>\n<em>Message<\/em> \u2012 payload<\/p>\n<p><\/p>\n<h2 id=\"inverted-publish-subscribe\">Inverted Publish-subscribe<\/h2>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Building blocks of distributed applications. The first approximation.\" src=\"\/wp-content\/uploads\/2019\/04\/2ef7cd7de459455937bdcfa8a11fd028.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>By implementing pub-sub, you can achieve a pattern convenient for logging. The set of sources and consumers can vary greatly. The diagram shows a case with one consumer and multiple sources.<\/p>\n<p><\/p>\n<h2 id=\"task-distribution-pattern\">Task distribution pattern<\/h2>\n<p><\/p>\n<p>In almost every project, there are tasks for deferred processing, such as report generation, notification delivery, and data retrieval from external systems. The throughput of the system carrying out these tasks can be easily scaled by adding handlers. All we need to do is create a cluster of handlers and distribute tasks evenly among them.<\/p>\n<p><\/p>\n<p>Let's consider the emerging situations with 3 handlers as an example. Even at the task distribution stage, the issue of equitable distribution and handler overflow arises. Round-robin distribution will ensure fairness, and to prevent handler overflow, we will introduce a limitation <em>prefetch_limit<\/em>. In transitional modes <em>prefetch_limit<\/em> it will prevent a single handler from receiving all tasks.<\/p>\n<p><\/p>\n<p>Messaging manages queues and processing priority. Handlers receive tasks as they arrive. The task execution may end successfully or with a failure:<\/p>\n<p><\/p>\n<ul>\n<li><code>messaging:ack(Tack)<\/code> \u2012 is called in the case of successful message processing<\/li>\n<li><code>messaging:nack(Tack)<\/code> \u2012 is called in all exceptional situations. After the task is returned, messaging will pass it to another handler.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Building blocks of distributed applications. The first approximation.\" src=\"\/wp-content\/uploads\/2019\/04\/c2c632b6411247ee8b0dcfb3250a4503.jpg\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Let's assume that during the processing of three tasks there was a complex failure: handler 1 crashed after receiving the task without having time to notify the exchange point. In this case, the exchange point will reassign the task to another handler after the ack timeout expires. Handler 3, for some reason, rejected the task and sent a nack, resulting in the task also being passed to another handler that successfully completed it.<\/p>\n<p><\/p>\n<h2 id=\"predvaritelnyy-itog\">Preliminary Summary<\/h2>\n<p><\/p>\n<p>We have discussed the basic building blocks of distributed systems and gained a fundamental understanding of their application in Erlang\/Elixir.<\/p>\n<p><\/p>\n<p>By combining basic templates, complex paradigms can be built to address emerging tasks.<\/p>\n<p><\/p>\n<p>In the final part of the series, we will discuss general issues related to service organization, routing, and load balancing, as well as the practical aspects of scalability and system resilience.<\/p>\n<p><\/p>\n<p>End of the second part.<\/p>\n<p><\/p>\n<p>Photo <noindex>Marius Christensen<\/noindex><br \/>\nIllustrations prepared using websequencediagrams.com<\/p>\n<p>Source: <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\/en\/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=\"en_US\" \/>\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\/en\/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\udd47Building Blocks of Distributed Applications. First Approach | ProHoster","description":"","canonical_url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/stroitelnye-bloki-raspredelennyh-prilozhenij-pervoe-priblizhenie","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"en_US","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\/en\/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\/en\/wp-json\/wp\/v2\/posts\/30908","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/comments?post=30908"}],"version-history":[{"count":1,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/30908\/revisions"}],"predecessor-version":[{"id":162009,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/30908\/revisions\/162009"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media\/22886"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=30908"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=30908"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=30908"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}