{"id":80035,"date":"2020-05-02T13:42:54","date_gmt":"2020-05-02T11:42:54","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny"},"modified":"2020-05-02T13:42:54","modified_gmt":"2020-05-02T11:42:54","slug":"udobnye-arhitekturnye-patterny","status":"publish","type":"post","link":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","title":{"rendered":"Convenient Architectural Patterns","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hello, Habr!<\/p>\n<p><\/p>\n<p>In light of the current events due to the coronavirus, a number of internet services have started to experience increased load. For example, <noindex><a rel=\"nofollow\" href=\"https:\/\/www.independent.co.uk\/life-style\/gadgets-and-tech\/news\/coronavirus-ocado-down-app-website-stockpiling-food-online-delivery-a9402216.html\">one of the retail chains in the United Kingdom simply halted its online ordering website<\/a><\/noindex>, as it could not handle the demands. It's not always possible to simply speed up a server by adding more powerful hardware; however, customer requests must be processed (or they'll go to competitors).<\/p>\n<p><\/p>\n<p>In this article, I'll briefly discuss popular practices that can help create a fast and fault-tolerant service. However, among the possible development schemes, I have selected only those that are currently <strong>easy to utilize<\/strong>. For each point, you either already have ready-made libraries available, or you can solve the task using a cloud platform.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1 id=\"gorizontalnoe-masshtabirovanie\">Horizontal Scaling<\/h1>\n<p><\/p>\n<p>The simplest and most well-known point. Conventionally, there are two commonly encountered load distribution schemes \u2014 horizontal and vertical scaling. <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%9C%D0%B0%D1%81%D1%88%D1%82%D0%B0%D0%B1%D0%B8%D1%80%D1%83%D0%B5%D0%BC%D0%BE%D1%81%D1%82%D1%8C#%D0%93%D0%BE%D1%80%D0%B8%D0%B7%D0%BE%D0%BD%D1%82%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BC%D0%B0%D1%81%D1%88%D1%82%D0%B0%D0%B1%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5\">In the first case,<\/a><\/noindex> you allow services to work in parallel, thus distributing the load among them. <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%9C%D0%B0%D1%81%D1%88%D1%82%D0%B0%D0%B1%D0%B8%D1%80%D1%83%D0%B5%D0%BC%D0%BE%D1%81%D1%82%D1%8C#%D0%92%D0%B5%D1%80%D1%82%D0%B8%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BC%D0%B0%D1%81%D1%88%D1%82%D0%B0%D0%B1%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5\">In the second case,<\/a><\/noindex> you order more powerful servers or optimize the code.<\/p>\n<p><\/p>\n<p>For example, I will take an abstract cloud file storage, that is, a kind of analog of OwnCloud, OneDrive, and so on.<\/p>\n<p><\/p>\n<p>The standard illustration of such a scheme is below; however, it merely demonstrates the complexity of the system. After all, we need to somehow synchronize the services. What happens if a user saves a file from a tablet and then wants to view it on a phone?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Convenient Architectural Patterns\" src=\"\/wp-content\/uploads\/2020\/05\/7512c6d8783f5e0a38cc0861b01e54c8.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nThe difference between the approaches: in vertical scaling, we are ready to increase the capacity of nodes, while in horizontal scaling \u2014 we add new nodes to distribute the load.<\/p>\n<p><\/p>\n<h1 id=\"cqrs\">CQRS<\/h1>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/martinfowler.com\/bliki\/CQRS.html\">Command Query Responsibility Segregation<\/a><\/noindex> is quite an important pattern, as it allows different clients to not only connect to different services but also receive the same event streams. Its advantages are not so obvious for a simple application, but it is extremely important (and simple) for a high-load service. Its essence: incoming and outgoing data streams should not intersect. In other words, you cannot send a request and expect a response; instead, you send a request to service A, but receive a response in service B.<\/p>\n<p><\/p>\n<p>The first bonus of this approach is the ability to break the connection (in the broad sense of the word) during the execution of a long request. For example, let's take a more or less standard sequence:<\/p>\n<p><\/p>\n<ol>\n<li>The client sent a request to the server.<\/li>\n<li>The server started a long process.<\/li>\n<li>The server replied to the client with the result.<\/li>\n<\/ol>\n<p><\/p>\n<p>Let's imagine that in step 2, the connection was interrupted (either the network was reconnected, or the user navigated to another page, terminating the connection). In this case, it will be difficult for the server to send a reply to the user with information about what was processed. By applying CQRS, the sequence will be slightly different:<\/p>\n<p><\/p>\n<ol>\n<li>The client subscribed to updates.<\/li>\n<li>The client sent a request to the server.<\/li>\n<li>The server responded 'request accepted'.<\/li>\n<li>The server replied with the result via the channel from step '1'.<\/li>\n<\/ol>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Convenient Architectural Patterns\" src=\"\/wp-content\/uploads\/2020\/05\/8a00ea93eecc7cc0754122857c5f90d0.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>As seen, the scheme is a bit more complex. Moreover, the intuitive request-response approach is absent here. However, as can be seen, a connection break during the request processing will not lead to an error. Furthermore, if the user is actually connected to the service from multiple devices (for example, from a mobile phone and a tablet), it can be arranged for the response to arrive on both devices.<\/p>\n<p><\/p>\n<p>Interestingly, the code for handling incoming messages becomes somewhat uniform (not 100%) for both events influenced by the client and for other events, including those from other clients.<\/p>\n<p><\/p>\n<p>However, in reality, we gain additional bonuses due to the fact that a unidirectional flow can be processed in a functional style (using RX and analogs). And this is a significant advantage, as essentially the application can be made fully reactive, while also applying a functional approach. This can significantly save resources on development and maintenance for complex applications.<\/p>\n<p><\/p>\n<p>If we combine this approach with horizontal scaling, we gain the ability to send requests to one server while receiving responses from another. Thus, the client can choose the service that is convenient for him, while the system internally will still be able to process events correctly.<\/p>\n<p><\/p>\n<h1 id=\"event-sourcing\">Event Sourcing<\/h1>\n<p><\/p>\n<p>As you know, one of the main features of a distributed system is the lack of a global time and a common critical section. For a single process, you can achieve synchronization (on the same mutexes) in which you are sure that no one else is executing this code. However, for a distributed system, such synchronization is dangerous, as it incurs overhead and undermines the very beauty of scaling \u2014 ultimately, all components will be waiting on one.<\/p>\n<p><\/p>\n<p>From this, we derive the important fact that a fast distributed system cannot be synchronized, as that would reduce performance. On the other hand, we often need a certain level of consistency among components. For this, we can utilize an approach with <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">eventual consistency<\/a><\/noindex>, which guarantees that in the absence of data changes, after some period of time following the last update ('eventually'), all requests will return the most recently updated value.<\/p>\n<p><\/p>\n<p>It's important to understand that for traditional databases, another approach commonly used is <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Consistency_model#Strict_consistency\">strong consistency<\/a><\/noindex>, where each node possesses the same information (this is often achieved when a transaction is considered committed only after a response from a second server). There are some relaxations due to isolation levels, but the overall essence remains the same \u2014 you can operate in a fully consistent world.<\/p>\n<p><\/p>\n<p>However, let's return to the original task. If part of the system can be constructed using <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">eventual consistency<\/a><\/noindex>, then the following scheme can be built.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Convenient Architectural Patterns\" src=\"\/wp-content\/uploads\/2020\/05\/33c80be6bc33bd897ca9b9e5525f9ae8.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Key features of this approach:<\/p>\n<p><\/p>\n<ul>\n<li>Each incoming request is placed in a single queue.<\/li>\n<li>During the processing of the request, the service can also place tasks into other queues.<\/li>\n<li>Each incoming event has an identifier (necessary for deduplication).<\/li>\n<li>The queue works ideologically on an 'append only' scheme. Elements cannot be deleted or rearranged from it.<\/li>\n<li>The queue operates on a FIFO scheme (forgive the tautology). If parallel execution is needed, objects should be moved into different queues at one of the stages.<\/li>\n<\/ul>\n<p><\/p>\n<p>I remind you that we are considering the case of an online file storage. In this case, the system will look something like this:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Convenient Architectural Patterns\" src=\"\/wp-content\/uploads\/2020\/05\/b95efb29545dcde1db9432d12d00a1b1.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>It's important to note that the services on the diagram do not necessarily mean a separate server. The process can be the same. What matters more is that ideologically, these elements are separated in such a way that horizontal scaling can be easily applied.<\/p>\n<p><\/p>\n<p>For two users, the diagram will look like this (services intended for different users are marked in different colors):<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Convenient Architectural Patterns\" src=\"\/wp-content\/uploads\/2020\/05\/4c4aadc8b9dd505c3ad743e69fee4fae.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>The benefits of such a combination include:<\/p>\n<p><\/p>\n<ul>\n<li>Information processing services are separated. Queues are also separate. If we need to increase the system's capacity, we simply need to launch more services on a greater number of servers.<\/li>\n<li>When we receive information from a user, we do not necessarily have to wait for the complete data to be saved. On the contrary, it is sufficient to respond with 'ok', and then gradually start the process. Additionally, the queue smooths out peaks, as adding a new object happens quickly, and the user does not have to wait for the entire cycle.<\/li>\n<li>For example, I added a deduplication service that attempts to merge identical files. If it takes a long time in 1% of cases, the client will hardly notice it (as mentioned above), which is a significant advantage, since we no longer require 100% speed and reliability.<\/li>\n<\/ul>\n<p><\/p>\n<p>However, the downsides are immediately apparent:<\/p>\n<p><\/p>\n<ul>\n<li>Our system has lost strict consistency. This means that if, for instance, you subscribe to different services, theoretically you could receive a different state (since one of the services may not have time to receive the notification from the internal queue). As another consequence, the system now lacks a common time. This means, for example, that you cannot simply sort all events by the time of arrival since the clocks between servers may not be synchronized (moreover, having the same time on two servers is a utopia).<\/li>\n<li>No events can now simply be rolled back (as could be done with a database). Instead, it is necessary to add a new event \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/49451237\/compensating-events-on-cqrs-es-architecture\">compensation event<\/a><\/noindex>, which will change the last state to the required one. As an example from a similar area: without rewriting history (which is detrimental in some cases), in git you cannot roll back a commit, but you can create a special <noindex><a rel=\"nofollow\" href=\"https:\/\/git-scm.com\/docs\/git-revert\">rollback commit<\/a><\/noindex>, which essentially just reverts to the previous state. However, both the erroneous commit and the rollback will remain in the history.<\/li>\n<li>The data schema may change from release to release; however, old events can no longer be updated to the new standard (as events cannot be modified in principle).<\/li>\n<\/ul>\n<p><\/p>\n<p>As we can see, Event Sourcing works excellently with CQRS. Moreover, implementing a system with efficient and convenient queues, while not separating data streams, is already challenging in itself, as it requires adding synchronization points that would negate the positive effect of the queues. By applying both approaches simultaneously, it is necessary to slightly adjust the program's code. In our case, when sending a file to the server, the response only returns 'ok', which means only that 'the file addition operation has been saved'. Formally, this does not mean that the data is already available on other devices (for example, the deduplication service may be rebuilding the index). However, after a while, the client will receive a notification stating 'file X has been saved'.<\/p>\n<p><\/p>\n<p>As a result:<\/p>\n<p><\/p>\n<ul>\n<li>The number of file upload statuses increases: instead of the classic 'file sent', we now receive two: 'file added to the server queue' and 'file saved in storage'. The latter means that other devices can already start receiving the file (allowing for the fact that queues operate at different speeds).<\/li>\n<li>Due to the fact that information about uploads now comes through different channels, we need to come up with solutions to obtain the status of file processing. As a result, unlike the classic request-response model, the client can be restarted during file processing, but the processing status will remain accurate. Moreover, this point works essentially out of the box. Thus, we are now more tolerant of failures.<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"sharding\">Sharding<\/h1>\n<p><\/p>\n<p>As already described above, systems with event sourcing lack strict consistency. This means we can use multiple storage systems without any synchronization between them. Approaching our task, we can:<\/p>\n<p><\/p>\n<ul>\n<li>Separate files by types. For instance, images\/videos can be decoded and a more efficient format selected.<\/li>\n<li>Separate accounts by country. Due to various laws, this may be necessary; however, this architectural scheme automatically provides for such a possibility.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Convenient Architectural Patterns\" src=\"\/wp-content\/uploads\/2020\/05\/dd310df700bc39c014d0105a3425fece.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>If you want to transfer data from one storage to another, standard tools won't suffice. Unfortunately, in this case, you need to stop the queue, perform the migration, and then restart it. Generally, data cannot be moved 'on the fly'; however, if the event queue is fully stored and you have snapshots of previous states of the storage, we can replay events as follows:<\/p>\n<p><\/p>\n<ul>\n<li>In Event Source, each event has its identifier (ideally non-decreasing). Therefore, we can add a field in the storage \u2014 id of the last processed element.<\/li>\n<li>We duplicate the queue so that all events can be processed for multiple independent storages (the first is the one where data is currently stored, and the second is new but currently empty). The second queue, of course, is not being processed yet.<\/li>\n<li>We start the second queue (i.e., begin replaying events).<\/li>\n<li>When the new queue is relatively empty (that is, the average time difference between adding an element and extracting it will be acceptable), you can start switching readers to the new storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>As you can see, there has been and still is no strict consistency in our system. There is only eventual consistency, which guarantees that events are processed in the same order (however, possibly with different delays). Using this, we can comparatively easily transfer data without stopping the system across the globe.<\/p>\n<p><\/p>\n<p>Thus, continuing our example of an online file storage, this architecture already provides us with a number of benefits:<\/p>\n<p><\/p>\n<ul>\n<li>We can move objects closer to users dynamically. This can enhance service quality. <\/li>\n<li>We can store part of the data within companies. For instance, Enterprise users often require their data to remain in controlled data centers (to avoid data leaks). With sharding, we can easily support this. The task becomes even simpler if the customer has a compatible cloud (for example, <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.microsoft.com\/en-gb\/azure-stack\/asdk\/asdk-what-is?view=azs-1910\">Azure self hosted<\/a><\/noindex>).<\/li>\n<li>The most important thing is that we don't have to do this. Initially, we would be perfectly fine with just one storage for all accounts (so we can start working faster). And the key feature of this system is that, while it is extensible, at the initial stage it is quite simple. There's no need to immediately write code that works with a million separate independent queues, etc. If necessary, that can be done in the future.<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"static-content-hosting\">Static Content Hosting<\/h1>\n<p><\/p>\n<p>This point may seem quite obvious, yet it is still essential for a reasonably standard high-load application. The idea is simple: all static content is served not from the same server where the application resides, but from special servers dedicated specifically for this purpose. As a result, these operations are performed faster (a typical nginx serves files more quickly and at a lower cost than a Java server). Plus, the CDN architecture (<noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Content_delivery_network\">Content Delivery Network<\/a><\/noindex>) allows us to place our files closer to end users, which positively impacts the convenience of working with the service.<\/p>\n<p><\/p>\n<p>The simplest and most standard example of static content is a set of scripts and images for a website. It's straightforward \u2014 they are known in advance, after which an archive is uploaded to the CDN servers, from where it is served to end users.<\/p>\n<p><\/p>\n<p>However, in practice, a similar approach to lambda architecture can be applied for static content. Let\u2019s return to our task (an online file storage) where we need to deliver files to users. The simplest straightforward solution is to make a service that performs all necessary checks (authorization, etc.) for each user request and then downloads the file directly from our storage. The main downside of this approach is that static content (a file with a certain revision is, in essence, static content) is served by the same server that contains the business logic. Instead, we can create the following scheme:<\/p>\n<p><\/p>\n<ul>\n<li>The server issues a URL for downloading. It can take the form of file_id + key, where key is a mini-digital signature granting access to the resource for the next twenty-four hours.<\/li>\n<li>File delivery is handled by a simple nginx with the following options:\n<ul>\n<li>Content caching. Since this service can be located on a separate server, we have allowed for the future possibility of storing all the most recently downloaded files on disk.<\/li>\n<li>Key verification at the moment of connection establishment<\/li>\n<\/ul>\n<\/li>\n<li>Optionally: streaming content processing. For example, if we compress all files in the service, we can unpack them directly in this module. As a result, IO operations are performed where they belong. A Java archiver can easily allocate a lot of unnecessary memory, but rewriting the service with business logic in conditional Rust\/C++ can also prove inefficient. In our case, we use different processes (or even services), allowing us to effectively separate business logic and IO operations.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Convenient Architectural Patterns\" src=\"\/wp-content\/uploads\/2020\/05\/4dead07d5824938e09b986192ccc8f5e.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>This scheme does not closely resemble the distribution of static content (since we do not offload the entire static package somewhere), yet in reality, this approach deals with delivering immutable data. Moreover, this scheme can be generalized to other cases where the content is not merely static but can be represented as a set of immutable and undeletable blocks (though they may be added).<\/p>\n<p><\/p>\n<p>As another example (for reinforcement): if you've worked with Jenkins\/TeamCity, you know that both solutions are written in Java. They represent Java processes responsible for both build orchestration and content management. In particular, they both have tasks such as 'transfer a file\/folder from the server.' For example: issuing artifacts, transferring source code (when the agent does not download the code directly from the repository, but the server does this), accessing logs. All these tasks differ in their IO load. This means that the server, responsible for complex business logic, also needs to efficiently handle large data streams. Interestingly, such an operation can be delegated to nginx using exactly the same scheme (except that a data key should be added to the request).<\/p>\n<p><\/p>\n<p>However, if we return to our system, it results in a similar scheme:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Convenient Architectural Patterns\" src=\"\/wp-content\/uploads\/2020\/05\/97cfbf5daaa6678a9eecc3169692f0fc.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>As you can see, the system has become radically more complex. It's no longer just a mini-process that stores files locally. Now, it requires not-so-simple support, version control of APIs, etc. Therefore, once all the diagrams are drawn, it's best to thoroughly assess whether the scalability of such costs is justified. However, if you want the ability to scale the system (including handling an even larger number of users), you'll have to consider such solutions. The upside is that the system architecture is now ready for increased load (almost every component can be cloned for horizontal scaling). The system can be updated without downtime (only a few operations may slow down slightly).<\/p>\n<p><\/p>\n<p>As I mentioned at the very beginning, a number of online services are now experiencing increased loads. Some of them have simply begun to fail. Essentially, the systems failed exactly when the business needed to generate revenue. Instead of delaying deliveries, instead of offering customers to 'schedule deliveries for the upcoming months,' the system just said 'go to the competitors.' This is essentially the cost of low performance: losses will occur precisely when profits would be highest.<\/p>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Conclusion<\/h1>\n<p><\/p>\n<p>All these approaches were known before. VK has long utilized the idea of Static Content Hosting for delivering images. Numerous online games use Sharding schemes to divide players by regions or to segment game locations (if the world is unified). The Event Sourcing approach is actively used in email services. Most trading applications, where continuous data arrives, are actually built on the CQRS approach to filter incoming data. Moreover, horizontal scaling has been applied in many services for quite some time.<\/p>\n<p><\/p>\n<p>However, the most important thing is that all these patterns have become very easy to apply in modern applications (if suitable, of course). Clouds offer Sharding and horizontal scaling right from the start, which is much easier than ordering separate dedicated servers in different data centers by yourself. CQRS has also become much easier, partly due to the development of libraries like RX. Ten years ago, a rare website could support such a thing. Event Sourcing can be set up incredibly easily thanks to ready-made containers with Apache Kafka. Ten years ago, this would have been an innovation; now it is commonplace. Similarly, Static Content Hosting has become even simpler due to more convenient technologies (including detailed documentation and a large knowledge base).<\/p>\n<p><\/p>\n<p>As a result, the implementation of several quite complex architectural patterns has now become much simpler, so it is worth considering them in advance. If a decade-old application rejected one of the solutions mentioned above due to the high cost of implementation and maintenance, now, in a new application, or after refactoring, it is possible to create a service that is architecturally both extensible (in terms of performance) and ready for new customer requests (for example, for localization of personal data).<\/p>\n<p><\/p>\n<p>And most importantly: please do not use these approaches if you have a simple application. Yes, they are beautiful and interesting, but for a site with peak traffic of 100 users, a classic monolith can often suffice (at least externally; internally everything can be broken into modules, etc.).<\/p>\n<p>Source: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/dbtc\/blog\/499758\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u0441\u0432\u0435\u0442\u0435 \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438\u0437-\u0437\u0430 \u043a\u043e\u0440\u043e\u043d\u0430\u0432\u0438\u0440\u0443\u0441\u0430 \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0442\u0430\u043b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0434\u043d\u0430 \u0438\u0437 \u0442\u043e\u0440\u0433\u043e\u0432\u044b\u0445 \u0441\u0435\u0442\u0435\u0439 \u0432 \u0412\u0435\u043b\u0438\u043a\u043e\u0431\u0440\u0438\u0442\u0430\u043d\u0438\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0430 \u0441\u0430\u0439\u0442 \u0441 \u043e\u043d\u043b\u0430\u0439\u043d-\u0437\u0430\u043a\u0430\u0437\u0430\u043c\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u043d\u0435 \u0445\u0432\u0430\u0442\u0438\u043b\u043e \u043c\u043e\u0449\u043d\u043e\u0441\u0442\u0435\u0439. \u0418 \u0434\u0430\u043b\u0435\u043a\u043e \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u043c\u043e\u0436\u043d\u043e \u0443\u0441\u043a\u043e\u0440\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440, \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0432 \u0431\u043e\u043b\u0435\u0435 \u043c\u043e\u0449\u043d\u043e\u0435 \u043e\u0431\u043e\u0440\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0435, \u043e\u0434\u043d\u0430\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043d\u0430\u0434\u043e (\u0438\u043b\u0438 \u043e\u043d\u0438 \u0443\u0439\u0434\u0443\u0442 \u043a \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u0430\u043c). \u0412 \u044d\u0442\u043e\u0439 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":80036,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-80035","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.2 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u0441\u0432\u0435\u0442\u0435 \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438\u0437-\u0437\u0430 \u043a\u043e\u0440\u043e\u043d\u0430\u0432\u0438\u0440\u0443\u0441\u0430 \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0442\u0430\u043b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443.\" \/>\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\/udobnye-arhitekturnye-patterny\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\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\u0423\u0434\u043e\u0431\u043d\u044b\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u0441\u0432\u0435\u0442\u0435 \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438\u0437-\u0437\u0430 \u043a\u043e\u0440\u043e\u043d\u0430\u0432\u0438\u0440\u0443\u0441\u0430 \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0442\u0430\u043b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny\" \/>\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=\"2020-05-02T11:42:54+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-05-02T11:42:54+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\udd47Convenient architectural patterns | ProHoster","description":"Hello, Habr! In light of current events due to the coronavirus, a number of internet services have started to experience increased load.","canonical_url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","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\u0423\u0434\u043e\u0431\u043d\u044b\u0435 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u043d\u044b\u0435 \u043f\u0430\u0442\u0442\u0435\u0440\u043d\u044b | ProHoster","og:description":"\u041f\u0440\u0438\u0432\u0435\u0442, \u0425\u0430\u0431\u0440! \u0412 \u0441\u0432\u0435\u0442\u0435 \u0442\u0435\u043a\u0443\u0449\u0438\u0445 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438\u0437-\u0437\u0430 \u043a\u043e\u0440\u043e\u043d\u0430\u0432\u0438\u0440\u0443\u0441\u0430 \u0440\u044f\u0434 \u0438\u043d\u0442\u0435\u0440\u043d\u0435\u0442-\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441\u0442\u0430\u043b \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443.","og:url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","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":"2020-05-02T11:42:54+00:00","article:modified_time":"2020-05-02T11:42:54+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"80035","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":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 16:26:40","updated":"2022-09-28 01:38:29","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\/80035","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=80035"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/80035\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media\/80036"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=80035"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=80035"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=80035"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}