{"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\/pl\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","title":{"rendered":"Wygodne wzorce architektoniczne","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Cze\u015b\u0107, Habr!<\/p>\n<p><\/p>\n<p>W \u015bwietle bie\u017c\u0105cych wydarze\u0144 zwi\u0105zanych z koronawirusem szereg us\u0142ug internetowych zacz\u0119\u0142o otrzymywa\u0107 zwi\u0119kszone obci\u0105\u017cenie. Na przyk\u0142ad, <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\">jedna z sieci handlowych w Wielkiej Brytanii po prostu wstrzyma\u0142a stron\u0119 z zam\u00f3wieniami online,<\/a><\/noindex>poniewa\u017c nie wystarczy\u0142o mocy obliczeniowej. I nie zawsze mo\u017cna przyspieszy\u0107 serwer, po prostu dodaj\u0105c mocniejszy sprz\u0119t, jednak\u017ce zapytania klient\u00f3w nale\u017cy przetwarza\u0107 (w przeciwnym razie odejd\u0105 do konkurencji).<\/p>\n<p><\/p>\n<p>W tym artykule kr\u00f3tko opisz\u0119 popularne praktyki, kt\u00f3re pozwol\u0105 stworzy\u0107 szybki i odporne na awarie us\u0142ugi. Wybra\u0142em jednak tylko te schematy rozwoju, kt\u00f3re teraz <strong>s\u0105 \u0142atwe do wykorzystania.<\/strong>Dla ka\u017cdego punktu ju\u017c masz gotowe biblioteki, lub masz mo\u017cliwo\u015b\u0107 rozwi\u0105zania problemu za pomoc\u0105 platformy chmurowej.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1 id=\"gorizontalnoe-masshtabirovanie\">Skalowanie horyzontalne<\/h1>\n<p><\/p>\n<p>Najprostszy i najbardziej znany punkt. Og\u00f3lnie rzecz bior\u0105c, najcz\u0119\u015bciej wyst\u0119puj\u0105 dwa schematy rozk\u0142adu obci\u0105\u017cenia \u2014 skalowanie horyzontalne i wertykalne. <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\">W pierwszym przypadku<\/a><\/noindex> pozwalasz us\u0142ugom dzia\u0142a\u0107 r\u00f3wnolegle, w ten spos\u00f3b rozk\u0142adaj\u0105c obci\u0105\u017cenie mi\u0119dzy nimi. <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\">W drugim<\/a><\/noindex> zamawiasz mocniejsze serwery lub optymalizujesz kod.<\/p>\n<p><\/p>\n<p>Na przyk\u0142ad we\u017amiemy abstrakcyjne chmurowe miejsce na pliki, czyli jaki\u015b odpowiednik OwnCloud, OneDrive itd.<\/p>\n<p><\/p>\n<p>Standardowy obrazek takiego schematu znajduje si\u0119 poni\u017cej, jednak\u017ce tylko pokazuje z\u0142o\u017cono\u015b\u0107 systemu. Musimy jako\u015b zsynchronizowa\u0107 us\u0142ugi. Co si\u0119 stanie, je\u015bli u\u017cytkownik zapisze plik na tablecie, a nast\u0119pnie chce go zobaczy\u0107 na telefonie?<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wygodne wzorce architektoniczne\" src=\"\/wp-content\/uploads\/2020\/05\/7512c6d8783f5e0a38cc0861b01e54c8.png\" style=\"display:block;margin: 0 auto;\" \/><br \/>\nR\u00f3\u017cnica mi\u0119dzy podej\u015bciami: przy skalowaniu wertykalnym jeste\u015bmy gotowi zwi\u0119ksza\u0107 moc w\u0119z\u0142\u00f3w, podczas gdy przy skalowaniu horyzontalnym \u2014 dodajemy nowe w\u0119z\u0142y, aby roz\u0142o\u017cy\u0107 obci\u0105\u017cenie.<\/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\">Segregacja odpowiedzialno\u015bci za polecenia i zapytania<\/a><\/noindex> to do\u015b\u0107 wa\u017cny wzorzec, poniewa\u017c umo\u017cliwia r\u00f3\u017cnym klientom nie tylko \u0142\u0105czenie si\u0119 z r\u00f3\u017cnymi us\u0142ugami, ale tak\u017ce uzyskiwanie tych samych strumieni zdarze\u0144. Jego korzy\u015bci nie s\u0105 tak oczywiste dla prostych aplikacji, jednak jest on niezmiernie wa\u017cny (i prosty) dla obci\u0105\u017conego serwisu. Jego istota: przychodz\u0105ce i wychodz\u0105ce strumienie danych nie powinny si\u0119 krzy\u017cowa\u0107. To znaczy, \u017ce nie mo\u017cesz wys\u0142a\u0107 zapytania i oczekiwa\u0107 odpowiedzi, zamiast tego wysy\u0142asz zapytanie do us\u0142ugi A, jednak otrzymujesz odpowied\u017a w us\u0142udze B.<\/p>\n<p><\/p>\n<p>Pierwsz\u0105 zalet\u0105 tego podej\u015bcia jest mo\u017cliwo\u015b\u0107 przerwania po\u0142\u0105czenia (w najszerszym tego s\u0142owa znaczeniu) w trakcie realizacji d\u0142ugiego zapytania. Dla przyk\u0142adu we\u017amy bardziej standardow\u0105 sekwencj\u0119:<\/p>\n<p><\/p>\n<ol>\n<li>Klient wys\u0142a\u0142 zapytanie do serwera.<\/li>\n<li>Serwer rozpocz\u0105\u0142 d\u0142ug\u0105 obr\u00f3bk\u0119.<\/li>\n<li>Serwer odpowiedzia\u0142 klientowi wynikiem.<\/li>\n<\/ol>\n<p><\/p>\n<p>Wyobra\u017amy sobie, \u017ce w punkcie 2 dosz\u0142o do przerwania po\u0142\u0105czenia (czy to sie\u0107 zosta\u0142a prze\u0142\u0105czona, czy u\u017cytkownik przeszed\u0142 na inn\u0105 stron\u0119, przerywaj\u0105c po\u0142\u0105czenie). W takim przypadku serwerowi b\u0119dzie trudno wys\u0142a\u0107 odpowied\u017a do u\u017cytkownika z informacj\u0105, co dok\u0142adnie zosta\u0142o przetworzone. Stosuj\u0105c CQRS, sekwencja b\u0119dzie nieco inna:<\/p>\n<p><\/p>\n<ol>\n<li>Klient zapisa\u0142 si\u0119 na aktualizacje.<\/li>\n<li>Klient wys\u0142a\u0142 zapytanie do serwera.<\/li>\n<li>Serwer odpowiedzia\u0142 'zapytanie przyj\u0119te'.<\/li>\n<li>Serwer odpowiedzia\u0142 wynikiem za po\u015brednictwem kana\u0142u z punktu '1'.<\/li>\n<\/ol>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wygodne wzorce architektoniczne\" src=\"\/wp-content\/uploads\/2020\/05\/8a00ea93eecc7cc0754122857c5f90d0.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Jak wida\u0107, schemat jest nieco bardziej skomplikowany. Co wi\u0119cej, intuicyjne podej\u015bcie request-response tutaj nie wyst\u0119puje. Jednak jak wida\u0107, przerwanie po\u0142\u0105czenia podczas przetwarzania zapytania nie prowadzi do b\u0142\u0119du. Co wi\u0119cej, je\u015bli u\u017cytkownik rzeczywi\u015bcie pod\u0142\u0105czy\u0142 si\u0119 do us\u0142ugi z kilku urz\u0105dze\u0144 (na przyk\u0142ad z telefonu kom\u00f3rkowego i tabletu), mo\u017cna zorganizowa\u0107 tak, aby odpowied\u017a przychodzi\u0142a na oba urz\u0105dzenia.<\/p>\n<p><\/p>\n<p>Co ciekawe, kod przetwarzania przychodz\u0105cych wiadomo\u015bci staje si\u0119 w pewnym sensie jednolity (nie w 100%) zar\u00f3wno dla zdarze\u0144, na kt\u00f3re wp\u0142ywa sam klient, jak i dla pozosta\u0142ych zdarze\u0144, w tym tych od innych klient\u00f3w.<\/p>\n<p><\/p>\n<p>Jednak w rzeczywisto\u015bci zyskujemy dodatkowe korzy\u015bci zwi\u0105zane z tym, \u017ce jednostronny strumie\u0144 mo\u017cna przetwarza\u0107 w stylu funkcyjnym (u\u017cywaj\u0105c RX i analogii). A to ju\u017c jest powa\u017cna zaleta, poniewa\u017c zasadniczo aplikacj\u0119 mo\u017cna uczyni\u0107 w pe\u0142ni reaktywn\u0105, z zastosowaniem podej\u015bcia funkcyjnego. Dla du\u017cych program\u00f3w mo\u017ce to znacznie zaoszcz\u0119dzi\u0107 zasoby na rozw\u00f3j i wsparcie.<\/p>\n<p><\/p>\n<p>Je\u015bli po\u0142\u0105czymy to podej\u015bcie z poziomym skalowaniem, zyskujemy mo\u017cliwo\u015b\u0107 wysy\u0142ania zapyta\u0144 do jednego serwera, a odbierania odpowiedzi od innego. W ten spos\u00f3b klient mo\u017ce sam wybiera\u0107 wygodn\u0105 dla siebie us\u0142ug\u0119, a system wewn\u0119trzny wci\u0105\u017c b\u0119dzie m\u00f3g\u0142 prawid\u0142owo przetwarza\u0107 zdarzenia.<\/p>\n<p><\/p>\n<h1 id=\"event-sourcing\">Event Sourcing<\/h1>\n<p><\/p>\n<p>Jak wiesz, jedn\u0105 z g\u0142\u00f3wnych cech systemu rozproszonego jest brak wsp\u00f3lnego czasu i wsp\u00f3lnej sekcji krytycznej. Dla jednego procesu mo\u017cna wprowadzi\u0107 synchronizacj\u0119 (na tych samych mutexach), w kt\u00f3rej masz pewno\u015b\u0107, \u017ce nikt inny nie wykonuje tego kodu. Jednak w systemie rozproszonym takie rozwi\u0105zanie jest niebezpieczne, poniewa\u017c wi\u0105\u017ce si\u0119 z dodatkowymi kosztami, a ca\u0142y urok skalowalno\u015bci zostaje zniweczony \u2014 wszyscy komponenty b\u0119d\u0105 czeka\u0142y na jeden.<\/p>\n<p><\/p>\n<p>St\u0105d wynika wa\u017cny fakt \u2014 szybkiego systemu rozproszonego nie mo\u017cna synchronizowa\u0107, poniewa\u017c wtedy zmniejszymy wydajno\u015b\u0107. Z drugiej strony cz\u0119sto potrzebna jest okre\u015blona sp\u00f3jno\u015b\u0107 komponent\u00f3w. W tym celu mo\u017cna zastosowa\u0107 podej\u015bcie z <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">sp\u00f3jn\u0105 form\u0105 ko\u0144cow\u0105<\/a><\/noindex>, gdzie gwarantuje si\u0119, \u017ce w przypadku braku zmian danych po pewnym czasie po ostatniej aktualizacji (\u201ew ko\u0144cu\u201d) wszystkie zapytania b\u0119d\u0105 zwraca\u0107 ostatni\u0105 zaktualizowan\u0105 warto\u015b\u0107.<\/p>\n<p><\/p>\n<p>Wa\u017cne jest, aby rozumie\u0107, \u017ce w klasycznych bazach danych stosuje si\u0119 do\u015b\u0107 cz\u0119sto <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Consistency_model#Strict_consistency\">surow\u0105 sp\u00f3jno\u015b\u0107<\/a><\/noindex>, gdzie ka\u017cdy w\u0119ze\u0142 ma te same informacje (takie wyniki cz\u0119sto osi\u0105ga si\u0119, gdy transakcja uznawana jest za zako\u0144czon\u0105 dopiero po odpowiedzi drugiego serwera). Istniej\u0105 pewne ust\u0119pstwa ze wzgl\u0119du na poziomy izolacji, jednak og\u00f3lna zasada pozostaje ta sama \u2014 mo\u017cesz \u017cy\u0107 w ca\u0142kowicie sp\u00f3jnym \u015bwiecie.<\/p>\n<p><\/p>\n<p>Jednak wr\u00f3\u0107my do pierwotnego zadania. Je\u015bli cz\u0119\u015b\u0107 systemu mo\u017ce by\u0107 zbudowana z <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">sp\u00f3jn\u0105 form\u0105 ko\u0144cow\u0105<\/a><\/noindex>, to mo\u017cna zbudowa\u0107 nast\u0119puj\u0105cy schemat.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wygodne wzorce architektoniczne\" src=\"\/wp-content\/uploads\/2020\/05\/33c80be6bc33bd897ca9b9e5525f9ae8.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wa\u017cne cechy tego podej\u015bcia:<\/p>\n<p><\/p>\n<ul>\n<li>Ka\u017cde przychodz\u0105ce zapytanie jest umieszczane w kolejce.<\/li>\n<li>Podczas przetwarzania zapytania us\u0142uga mo\u017ce r\u00f3wnie\u017c umieszcza\u0107 zadania w innych kolejkach.<\/li>\n<li>Ka\u017cde przychodz\u0105ce zdarzenie ma identyfikator (konieczny do de-duplikacji).<\/li>\n<li>Kolejka ideologicznie dzia\u0142a na zasadzie &quot;tylko dodawanie&quot;. Nie mo\u017cna usuwa\u0107 element\u00f3w ani przestawia\u0107 ich.<\/li>\n<li>Kolejka dzia\u0142a na zasadzie FIFO (przepraszam za tautologi\u0119). Je\u015bli konieczne jest r\u00f3wnoleg\u0142e przetwarzanie, to na jednym z etap\u00f3w nale\u017cy przenie\u015b\u0107 obiekty do r\u00f3\u017cnych kolejek.<\/li>\n<\/ul>\n<p><\/p>\n<p>Przypominam, \u017ce rozwa\u017camy przypadek internetowego magazynu plik\u00f3w. W tym przypadku system b\u0119dzie wygl\u0105da\u0142 mniej wi\u0119cej tak:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wygodne wzorce architektoniczne\" src=\"\/wp-content\/uploads\/2020\/05\/b95efb29545dcde1db9432d12d00a1b1.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Wa\u017cne jest, \u017ce us\u0142ugi na diagramie niekoniecznie oznaczaj\u0105 oddzielny serwer. Nawet proces mo\u017ce by\u0107 ten sam. Wa\u017cne jest co\u015b innego: ideologicznie te rzeczy s\u0105 podzielone w taki spos\u00f3b, aby mo\u017cna by\u0142o \u0142atwo zastosowa\u0107 poziome skalowanie.<\/p>\n<p><\/p>\n<p>A dla dw\u00f3ch u\u017cytkownik\u00f3w diagram b\u0119dzie wygl\u0105da\u0142 tak (us\u0142ugi przeznaczone dla r\u00f3\u017cnych u\u017cytkownik\u00f3w oznaczone s\u0105 r\u00f3\u017cnymi kolorami):<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wygodne wzorce architektoniczne\" src=\"\/wp-content\/uploads\/2020\/05\/4c4aadc8b9dd505c3ad743e69fee4fae.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Korzy\u015bci z takiej kombinacji:<\/p>\n<p><\/p>\n<ul>\n<li>Us\u0142ugi przetwarzania informacji s\u0105 podzielone. Kolejki r\u00f3wnie\u017c s\u0105 podzielone. Je\u015bli b\u0119dziemy musieli zwi\u0119kszy\u0107 przepustowo\u015b\u0107 systemu, wystarczy uruchomi\u0107 wi\u0119cej us\u0142ug na wi\u0119kszej liczbie serwer\u00f3w.<\/li>\n<li>Kiedy otrzymujemy informacje od u\u017cytkownika, nie musimy czeka\u0107 na pe\u0142ne zapisanie danych. Przeciwnie, wystarczy odpowiedzie\u0107 \"ok\", a nast\u0119pnie stopniowo zacz\u0105\u0107 prac\u0119. Po drodze kolejka wyg\u0142adza szczyty, poniewa\u017c dodanie nowego obiektu zachodzi szybko, a u\u017cytkownik nie musi czeka\u0107 na pe\u0142ne przej\u015bcie przez ca\u0142y cykl.<\/li>\n<li>Dla przyk\u0142adu doda\u0142em us\u0142ug\u0119 deduplikacji, kt\u00f3ra stara si\u0119 \u0142\u0105czy\u0107 identyczne pliki. Je\u015bli dzia\u0142a ona d\u0142ugo w 1% przypadk\u00f3w, klient tego praktycznie nie zauwa\u017cy (patrz powy\u017cej), co jest du\u017cym plusem, poniewa\u017c nie wymagamy ju\u017c stuprocentowej pr\u0119dko\u015bci i niezawodno\u015bci.<\/li>\n<\/ul>\n<p><\/p>\n<p>Jednak od razu wida\u0107 r\u00f3wnie\u017c minusy:<\/p>\n<p><\/p>\n<ul>\n<li>Nasza system straci\u0142 \u015bcis\u0142\u0105 sp\u00f3jno\u015b\u0107. Oznacza to, \u017ce je\u015bli na przyk\u0142ad zasubskrybujemy r\u00f3\u017cne us\u0142ugi, teoretycznie mo\u017cna otrzyma\u0107 r\u00f3\u017cny stan (poniewa\u017c jedna z us\u0142ug mo\u017ce nie zd\u0105\u017cy\u0107 przyj\u0105\u0107 powiadomienia z wewn\u0119trznej kolejki). Jako kolejne konsekwencje, system nie ma teraz wsp\u00f3lnego czasu. To znaczy, \u017ce nie mo\u017cna na przyk\u0142ad posortowa\u0107 wszystkich zdarze\u0144 tylko po czasie przybycia, poniewa\u017c zegary mi\u0119dzy serwerami mog\u0105 nie by\u0107 zsynchronizowane (ponadto ta sama godzina na dw\u00f3ch serwerach to utopia).<\/li>\n<li>\u017badne zdarzenia nie mog\u0105 by\u0107 teraz po prostu cofni\u0119te (jak w przypadku bazy danych). Zamiast tego nale\u017cy doda\u0107 nowe zdarzenie \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/49451237\/compensating-events-on-cqrs-es-architecture\">compensation event<\/a><\/noindex>, kt\u00f3re zmieni ostatni stan na wymagany. Jako przyk\u0142ad z podobnej dziedziny: bez przepisania historii (co jest z\u0142e w wielu przypadkach) w git nie mo\u017cna cofn\u0105\u0107 commita, jednak mo\u017cna zrobi\u0107 specjalny <noindex><a rel=\"nofollow\" href=\"https:\/\/git-scm.com\/docs\/git-revert\">rollback commit<\/a><\/noindex>, kt\u00f3ry w zasadzie po prostu przywr\u00f3ci poprzedni stan. Jednak w historii pozostanie zar\u00f3wno b\u0142\u0119dny commit, jak i rollback.<\/li>\n<li>Schemat danych mo\u017ce zmienia\u0107 si\u0119 z wydania na wydanie, jednak stare zdarzenia nie mog\u0105 by\u0107 teraz aktualizowane do nowego standardu (poniewa\u017c zdarze\u0144 zasadniczo nie mo\u017cna zmienia\u0107).<\/li>\n<\/ul>\n<p><\/p>\n<p>Jak wida\u0107, Event Sourcing doskonale wsp\u00f3\u0142pracuje z CQRS. Co wi\u0119cej, zrealizowanie systemu z efektywnymi i wygodnymi kolejkami, jednak bez rozdzielenia przep\u0142yw\u00f3w danych, ju\u017c samo w sobie jest trudne, poniewa\u017c konieczne b\u0119dzie dodanie punkt\u00f3w synchronizacji, kt\u00f3re zniweluj\u0105 ca\u0142y pozytywny efekt kolejek. Stosuj\u0105c oba podej\u015bcia jednocze\u015bnie, konieczne jest niewielkie dostosowanie kodu pracy programu. W naszym przypadku, przy wysy\u0142aniu pliku na serwer, w odpowiedzi otrzymujemy tylko \u201eok\u201d, co oznacza jedynie, \u017ce \u201eoperacja dodania pliku zosta\u0142a zapisana\u201d. Formalnie nie oznacza to, \u017ce dane s\u0105 ju\u017c dost\u0119pne na innych urz\u0105dzeniach (na przyk\u0142ad, us\u0142uga deduplikacji mo\u017ce odbudowywa\u0107 indeks). Jednak po pewnym czasie klient otrzyma powiadomienie w stylu \u201eplik X zosta\u0142 zapisany\u201d.<\/p>\n<p><\/p>\n<p>W rezultacie:<\/p>\n<p><\/p>\n<ul>\n<li>Liczba status\u00f3w wysy\u0142ania plik\u00f3w ro\u015bnie: zamiast klasycznego \u201eplik wys\u0142any\u201d otrzymujemy dwa: \u201eplik dodany do kolejki na serwerze\u201d i \u201eplik zapisany w magazynie\u201d. Ostatnie oznacza, \u017ce inne urz\u0105dzenia mog\u0105 ju\u017c zaczyna\u0107 pobiera\u0107 plik (z uwzgl\u0119dnieniem, \u017ce kolejki dzia\u0142aj\u0105 z r\u00f3\u017cn\u0105 szybko\u015bci\u0105).<\/li>\n<li>Z powodu tego, \u017ce informacje o wys\u0142aniu teraz przychodz\u0105 r\u00f3\u017cnymi kana\u0142ami, musimy wymy\u015bli\u0107 rozwi\u0105zania, aby otrzyma\u0107 status przetwarzania pliku. W konsekwencji: w przeciwie\u0144stwie do klasycznego request-response, klient mo\u017ce by\u0107 uruchamiany na nowo w trakcie przetwarzania pliku, jednak status tego przetwarzania b\u0119dzie poprawny. Co wi\u0119cej, ten punkt dzia\u0142a, w zasadzie, od razu. W rezultacie: jeste\u015bmy teraz bardziej tolerancyjni na awarie.<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"sharding\">Sharding<\/h1>\n<p><\/p>\n<p>Jak ju\u017c opisano powy\u017cej, w systemach z event sourcing brak jest rygorystycznej sp\u00f3jno\u015bci. Oznacza to, \u017ce mo\u017cemy korzysta\u0107 z kilku magazyn\u00f3w bez jakiejkolwiek synchronizacji mi\u0119dzy nimi. Zbli\u017caj\u0105c si\u0119 do naszego zadania, mo\u017cemy:<\/p>\n<p><\/p>\n<ul>\n<li>Separowa\u0107 pliki wed\u0142ug typ\u00f3w. Na przyk\u0142ad, obrazy\/wideo mo\u017cna dekodowa\u0107 i wybiera\u0107 bardziej efektywny format.<\/li>\n<li>Rozdzielanie kont wed\u0142ug kraj\u00f3w. W zwi\u0105zku z wieloma przepisami tego typu mo\u017ce by\u0107 to konieczne, jednak ta architektura daje tak\u0105 mo\u017cliwo\u015b\u0107 automatycznie.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wygodne wzorce architektoniczne\" src=\"\/wp-content\/uploads\/2020\/05\/dd310df700bc39c014d0105a3425fece.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Je\u015bli chcesz przenie\u015b\u0107 dane z jednego magazynu do drugiego, wtedy standardowe \u015brodki nie wystarcz\u0105. Niestety, w takim przypadku konieczne jest wstrzymanie kolejki, wykonanie migracji, a nast\u0119pnie jej uruchomienie. W og\u00f3lnym przypadku \u201ena \u017cywo\u201d danych nie da si\u0119 przenie\u015b\u0107, jednak je\u015bli kolejka zdarze\u0144 jest przechowywana w ca\u0142o\u015bci, a Ty masz migawki poprzednich stan\u00f3w magazynu, mo\u017cemy odtworzy\u0107 zdarzenia w nast\u0119puj\u0105cy spos\u00f3b:<\/p>\n<p><\/p>\n<ul>\n<li>W \u0179r\u00f3dle Zdarze\u0144 ka\u017cde zdarzenie ma sw\u00f3j identyfikator (w idealnym przypadku - niezmniejszaj\u0105cy si\u0119). A wi\u0119c w magazynie mo\u017cemy doda\u0107 pole - id ostatniego przetworzonego elementu.<\/li>\n<li>Duplikujemy kolejk\u0119, aby wszystkie zdarzenia mog\u0142y by\u0107 przetwarzane dla kilku niezale\u017cnych magazyn\u00f3w (pierwszy to ten, w kt\u00f3rym ju\u017c teraz znajduj\u0105 si\u0119 dane, a drugi to nowy, lecz na razie pusty). Druga kolejka, naturalnie, na razie nie jest przetwarzana.<\/li>\n<li>Uruchamiamy drug\u0105 kolejk\u0119 (to znaczy zaczynamy odtwarzanie zdarze\u0144).<\/li>\n<li>Kiedy nowa kolejka b\u0119dzie wzgl\u0119dnie pusta (tzn. \u015brednia r\u00f3\u017cnica w czasie mi\u0119dzy dodaniem elementu a jego wyci\u0105gni\u0119ciem b\u0119dzie akceptowalna), mo\u017cna zacz\u0105\u0107 prze\u0142\u0105cza\u0107 czytelnik\u00f3w na nowy magazyn.<\/li>\n<\/ul>\n<p><\/p>\n<p>Jak wida\u0107, w naszym systemie nie by\u0142o i nie ma \u015bcis\u0142ej sp\u00f3jno\u015bci. Jest tylko eventualna sp\u00f3jno\u015b\u0107, czyli gwarancja, \u017ce zdarzenia s\u0105 przetwarzane w tym samym porz\u0105dku (mo\u017ce to jednak nast\u0105pi\u0107 z r\u00f3\u017cnym op\u00f3\u017anieniem). Wykorzystuj\u0105c to, mo\u017cemy stosunkowo \u0142atwo przenie\u015b\u0107 dane bez zatrzymywania systemu na drugi koniec \u015bwiata.<\/p>\n<p><\/p>\n<p>W ten spos\u00f3b, kontynuuj\u0105c nasz przyk\u0142ad dotycz\u0105cy online magazynu dla plik\u00f3w, taka architektura daje nam szereg bonus\u00f3w:<\/p>\n<p><\/p>\n<ul>\n<li>Mo\u017cemy przenosi\u0107 obiekty bli\u017cej u\u017cytkownik\u00f3w, w spos\u00f3b dynamiczny. Dzi\u0119ki temu mo\u017cna poprawi\u0107 jako\u015b\u0107 us\u0142ugi. <\/li>\n<li>Mo\u017cemy przechowywa\u0107 cz\u0119\u015b\u0107 danych w obr\u0119bie firm. Na przyk\u0142ad u\u017cytkownicy klasy Enterprise cz\u0119sto wymagaj\u0105 przechowywania swoich danych w kontrolowanych centrach danych (aby unikn\u0105\u0107 wyciek\u00f3w danych). Dzi\u0119ki shardingowi mo\u017cemy to \u0142atwo wspiera\u0107. A zadanie staje si\u0119 jeszcze prostsze, je\u015bli klient ma zgodne chmury (na przyk\u0142ad, <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>A najwa\u017cniejsze \u2014 mo\u017cemy tego nie robi\u0107. Na pocz\u0105tku wystarczy\u0142oby jedno repozytorium dla wszystkich kont (by szybciej zacz\u0105\u0107 prac\u0119). Kluczow\u0105 cech\u0105 tego systemu jest to, \u017ce cho\u0107 jest rozbudowany, na pocz\u0105tkowym etapie jest wystarczaj\u0105co prosty. Po prostu nie trzeba od razu pisa\u0107 kodu, kt\u00f3ry dzia\u0142a z milionem niezale\u017cnych kolejek itd. Je\u015bli zajdzie taka potrzeba, mo\u017cna to zrobi\u0107 w przysz\u0142o\u015bci.<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"static-content-hosting\">Hosting tre\u015bci statycznych<\/h1>\n<p><\/p>\n<p>Ten punkt mo\u017ce wydawa\u0107 si\u0119 oczywisty, jednak jest konieczny dla wzgl\u0119dnie standardowej aplikacji obci\u0105\u017conej ruchem. Jego istota jest prosta: ca\u0142a tre\u015b\u0107 statyczna jest dostarczana nie z tego samego serwera, na kt\u00f3rym znajduje si\u0119 aplikacja, ale z specjalnych, przeznaczonych do tego serwer\u00f3w. W rezultacie te operacje s\u0105 szybsze (przyk\u0142adowo nginx dostarcza pliki szybciej i w spos\u00f3b mniej kosztowny ni\u017c serwer Java). Ponadto architektura CDN (<noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Content_delivery_network\">Content Delivery Network<\/a><\/noindex>) pozwala na umieszczanie naszych plik\u00f3w bli\u017cej ko\u0144cowych u\u017cytkownik\u00f3w, co pozytywnie wp\u0142ywa na wygod\u0119 korzystania z us\u0142ugi.<\/p>\n<p><\/p>\n<p>Najprostszy i najstandardowy przyk\u0142ad tre\u015bci statycznej to zestaw skrypt\u00f3w i obrazk\u00f3w dla strony internetowej. Z nimi wszystko jest proste \u2014 s\u0105 znane z g\u00f3ry, nast\u0119pnie archiwum \u0142adowane jest na serwery CDN, sk\u0105d jest dostarczane do ko\u0144cowych u\u017cytkownik\u00f3w.<\/p>\n<p><\/p>\n<p>Jednak w rzeczywisto\u015bci dla tre\u015bci statycznej mo\u017cna zastosowa\u0107 podej\u015bcie podobne do architektury lambda. Wr\u00f3\u0107my do naszego zadania (internetowe repozytorium plik\u00f3w), w kt\u00f3rym musimy dostarcza\u0107 pliki u\u017cytkownikom. Najprostsze rozwi\u0105zanie to zbudowanie us\u0142ugi, kt\u00f3ra dla ka\u017cdego zapytania u\u017cytkownika wykonuje wszystkie niezb\u0119dne sprawdzenia (autoryzacja itd.), a nast\u0119pnie pobiera plik bezpo\u015brednio z naszego repozytorium. G\u0142\u00f3wn\u0105 wad\u0105 takiego podej\u015bcia jest to, \u017ce tre\u015b\u0107 statyczna (a plik z okre\u015blon\u0105 rewizj\u0105 w zasadzie jest tre\u015bci\u0105 statyczn\u0105) jest dostarczana z tego samego serwera, kt\u00f3ry zawiera logik\u0119 biznesow\u0105. Zamiast tego mo\u017cna stworzy\u0107 nast\u0119puj\u0105c\u0105 schemat:<\/p>\n<p><\/p>\n<ul>\n<li>Serwer wydaje URL do pobrania. Mo\u017ce on mie\u0107 posta\u0107 file_id + key, gdzie key to mini cyfrowy podpis, kt\u00f3ry daje prawo dost\u0119pu do zasobu na najbli\u017csze 24 godziny.<\/li>\n<li>Rozdzielaniem pliku zajmuje si\u0119 prosty nginx z nast\u0119puj\u0105cymi opcjami:\n<ul>\n<li>Cache'owanie tre\u015bci. Poniewa\u017c ta us\u0142uga mo\u017ce znajdowa\u0107 si\u0119 na oddzielnym serwerze, zostawili\u015bmy sobie zapas na przysz\u0142o\u015b\u0107 z mo\u017cliwo\u015bci\u0105 przechowywania wszystkich ostatnich pobranych plik\u00f3w na dysku.<\/li>\n<li>Sprawdzanie klucza w momencie nawi\u0105zywania po\u0142\u0105czenia<\/li>\n<\/ul>\n<\/li>\n<li>Opcjonalnie: przetwarzanie strumieniowe tre\u015bci. Na przyk\u0142ad, je\u015bli kompresujemy wszystkie pliki w us\u0142udze, mo\u017cna przeprowadzi\u0107 dekompresj\u0119 bezpo\u015brednio w tym module. W efekcie: operacje IO s\u0105 wykonywane tam, gdzie maj\u0105 swoje miejsce. Archiwizator w Javie \u0142atwo mo\u017ce przydzieli\u0107 du\u017c\u0105 ilo\u015b\u0107 niepotrzebnej pami\u0119ci, jednak przepisanie us\u0142ugi z logik\u0105 biznesow\u0105 na warunkowe Rust\/C++ mo\u017ce okaza\u0107 si\u0119 r\u00f3wnie\u017c nieefektywne. W naszym przypadku u\u017cywane s\u0105 r\u00f3\u017cne procesy (lub nawet us\u0142ugi), dlatego mo\u017cna do\u015b\u0107 efektywnie oddzieli\u0107 logik\u0119 biznesow\u0105 od operacji IO.<\/li>\n<\/ul>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wygodne wzorce architektoniczne\" src=\"\/wp-content\/uploads\/2020\/05\/4dead07d5824938e09b986192ccc8f5e.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Tego typu schemat nie przypomina zbytnio dystrybucji statycznej tre\u015bci (poniewa\u017c nie przenosimy ca\u0142ego pakietu statyki gdzie\u015b), jednak w rzeczywisto\u015bci taki spos\u00f3b zajmuje si\u0119 dystrybucj\u0105 niezmiennych danych. Co wi\u0119cej, ten schemat mo\u017cna uog\u00f3lnia\u0107 na inne przypadki, kiedy tre\u015b\u0107 nie jest tylko statyczna, ale mo\u017ce by\u0107 przedstawiona w postaci zbioru niezmiennych i nieusuwalnych blok\u00f3w (chocia\u017c mog\u0105 by\u0107 one dodawane).<\/p>\n<p><\/p>\n<p>Jako kolejny przyk\u0142ad (w celu utrwalenia): je\u015bli pracowa\u0142e\u015b z Jenkins\/TeamCity, to wiesz, \u017ce oba rozwi\u0105zania zosta\u0142y napisane w Javie. Oba stanowi\u0105 proces Java, kt\u00f3ry zajmuje si\u0119 zar\u00f3wno orkiestracj\u0105 bud\u00f3w, jak i zarz\u0105dzaniem tre\u015bci\u0105. W szczeg\u00f3lno\u015bci obaj maj\u0105 zadania typu \"przekaza\u0107 plik\/katalog z serwera\". Na przyk\u0142ad: wydanie artefakt\u00f3w, przekazanie kodu \u017ar\u00f3d\u0142owego (kiedy agent nie pobiera kodu bezpo\u015brednio z repozytorium, lecz robi to serwer), dost\u0119p do log\u00f3w. Wszystkie te zadania r\u00f3\u017cni\u0105 si\u0119 obci\u0105\u017ceniem IO. Oznacza to, \u017ce serwer odpowiedzialny za skomplikowan\u0105 logik\u0119 biznesow\u0105 musi umie\u0107 efektywnie przepycha\u0107 przez siebie du\u017ce strumienie danych. Co najciekawsze, podobn\u0105 operacj\u0119 mo\u017cna delegowa\u0107 temu samemu nginx'owi wed\u0142ug dok\u0142adnie tego samego schematu (chyba \u017ce w \u017c\u0105daniu nale\u017cy doda\u0107 klucz danych).<\/p>\n<p><\/p>\n<p>Jednak je\u015bli wr\u00f3cimy do naszego systemu, to otrzymujemy podobny schemat:<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Wygodne wzorce architektoniczne\" src=\"\/wp-content\/uploads\/2020\/05\/97cfbf5daaa6678a9eecc3169692f0fc.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Jak wida\u0107, system radykalnie si\u0119 skomplikowa\u0142. Teraz to ju\u017c nie tylko mini-proces, kt\u00f3ry przechowuje pliki lokalnie. Teraz potrzebne jest ju\u017c nie tak proste wsparcie, kontrola wersji API itd. Dlatego po narysowaniu wszystkich diagram\u00f3w najlepiej szczeg\u00f3\u0142owo oceni\u0107, czy rozszerzalno\u015b\u0107 podobnych koszt\u00f3w jest op\u0142acalna. Je\u015bli jednak chcesz mie\u0107 mo\u017cliwo\u015b\u0107 rozszerzania systemu (w tym do obs\u0142ugi jeszcze wi\u0119kszej liczby u\u017cytkownik\u00f3w), b\u0119dziesz musia\u0142 zdecydowa\u0107 si\u0119 na takie rozwi\u0105zania. Z drugiej strony, jako wynik, system architektonicznie jest gotowy na zwi\u0119kszenie obci\u0105\u017cenia (praktycznie ka\u017cdy komponent mo\u017cna klonowa\u0107 dla horyzontalnego skalowania). System mo\u017cna aktualizowa\u0107 bez jego zatrzymywania (tylko niekt\u00f3re operacje b\u0119d\u0105 nieznacznie spowolnione).<\/p>\n<p><\/p>\n<p>Jak ju\u017c m\u00f3wi\u0142em na pocz\u0105tku, wiele internetowych us\u0142ug zacz\u0119\u0142o do\u015bwiadcza\u0107 zwi\u0119kszonego obci\u0105\u017cenia. I niekt\u00f3re z nich po prostu przesta\u0142y dzia\u0142a\u0107 poprawnie. W zasadzie systemy zawiod\u0142y dok\u0142adnie w momencie, gdy biznes powinien generowa\u0107 zyski. To znaczy, zamiast odroczonej dostawy, zamiast proponowa\u0107 klientom, aby \u201ezaplanowali dostaw\u0119 na najbli\u017csze miesi\u0105ce\u201d, system po prostu powiedzia\u0142 \u201eid\u017acie do konkurencji\u201d. W\u0142a\u015bnie taka jest cena niskiej wydajno\u015bci: straty wyst\u0105pi\u0105 dok\u0142adnie wtedy, gdy zyski by\u0142yby najwy\u017csze.<\/p>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Podsumowanie<\/h1>\n<p><\/p>\n<p>Wszystkie te podej\u015bcia by\u0142y znane ju\u017c wcze\u015bniej. Ten sam VK od dawna wykorzystuje pomys\u0142 Static Content Hosting do dostarczania obrazk\u00f3w. Mn\u00f3stwo gier online u\u017cywa schematu Sharding do podzia\u0142u graczy na regiony lub do podzia\u0142u lokacji gry (je\u015bli \u015bwiat jest jedno\u015bci\u0105). Podej\u015bcie Event Sourcing jest aktywnie wykorzystywane w poczcie elektronicznej. Wi\u0119kszo\u015b\u0107 aplikacji trader\u00f3w, w kt\u00f3rych stale nap\u0142ywaj\u0105 dane, tak naprawd\u0119 oparta jest na podej\u015bciu CQRS, aby m\u00f3c filtrowa\u0107 otrzymywane dane. Horyzontalne skalowanie by\u0142o stosowane od d\u0142u\u017cszego czasu w wielu us\u0142ugach.<\/p>\n<p><\/p>\n<p>Jednak najwa\u017cniejsze jest to, \u017ce wszystkie te wzorce sta\u0142y si\u0119 bardzo \u0142atwe do zastosowania w nowoczesnych aplikacjach (o ile s\u0105 na miejscu, oczywi\u015bcie). Chmury oferuj\u0105 Sharding i poziome skalowanie od razu, co jest znacznie \u0142atwiejsze ni\u017c zamawianie r\u00f3\u017cnych dedykowanych serwer\u00f3w w r\u00f3\u017cnych centrach danych samodzielnie. CQRS sta\u0142 si\u0119 znacznie prostszy, chocia\u017cby z powodu rozwoju bibliotek, takich jak RX. Oko\u0142o 10 lat temu rzadko kt\u00f3ra strona internetowa mog\u0142a by to obs\u0142u\u017cy\u0107. Event Sourcing r\u00f3wnie\u017c konfiguruje si\u0119 niesamowicie \u0142atwo dzi\u0119ki gotowym kontenerom z Apache Kafka. Oko\u0142o 10 lat temu by\u0142oby to innowacj\u0105, teraz to codzienno\u015b\u0107. Podobnie jest z hostingiem tre\u015bci statycznych: dzi\u0119ki wygodniejszym technologiom (w tym dlatego, \u017ce istnieje szczeg\u00f3\u0142owa dokumentacja i du\u017ca baza odpowiedzi), takie podej\u015bcie sta\u0142o si\u0119 jeszcze prostsze.<\/p>\n<p><\/p>\n<p>W rezultacie wdro\u017cenie szeregu do\u015b\u0107 z\u0142o\u017conych wzorc\u00f3w architektonicznych sta\u0142o si\u0119 teraz znacznie \u0142atwiejsze, co oznacza, \u017ce warto si\u0119 temu wcze\u015bniej przyjrze\u0107. Je\u015bli w aplikacji o dziesi\u0119cioletniej historii zrezygnowano z jednego z wy\u017cej wymienionych rozwi\u0105za\u0144 z powodu wysokich koszt\u00f3w wdro\u017cenia i eksploatacji, to teraz, w nowej aplikacji, lub po refaktoryzacji, mo\u017cna stworzy\u0107 serwis, kt\u00f3ry architektonicznie b\u0119dzie zar\u00f3wno elastyczny (z punktu widzenia wydajno\u015bci), jak i gotowy na nowe wymagania ze strony klient\u00f3w (na przyk\u0142ad w celu lokalizacji danych osobowych).<\/p>\n<p><\/p>\n<p>I najwa\u017cniejsze: prosz\u0119, nie stosujcie tych podej\u015b\u0107, je\u015bli macie prost\u0105 aplikacj\u0119. Tak, s\u0105 pi\u0119kne i interesuj\u0105ce, jednak dla strony z maksymalnym ruchem na poziomie 100 os\u00f3b cz\u0119sto mo\u017cna ograniczy\u0107 si\u0119 do klasycznego monolitu (przynajmniej na zewn\u0105trz, wewn\u0105trz wszystko mo\u017cna podzieli\u0107 na modu\u0142y itd.).<\/p>\n<p>\u0179r\u00f3d\u0142o: <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.1.1 - 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\/pl\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny\" \/>\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\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\/pl\/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\udd47Wygodne wzorce architektoniczne | ProHoster","description":"Cze\u015b\u0107, Habr! W \u015bwietle bie\u017c\u0105cych wydarze\u0144 zwi\u0105zanych z koronawirusem szereg us\u0142ug internetowych zacz\u0119\u0142o otrzymywa\u0107 zwi\u0119kszone obci\u0105\u017cenie.","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","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\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\/pl\/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\/pl\/wp-json\/wp\/v2\/posts\/80035","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=80035"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/80035\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media\/80036"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=80035"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=80035"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=80035"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}