{"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\/nl\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","title":{"rendered":"Convenient architectural patterns","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hallo, Habr!<\/p>\n<p><\/p>\n<p>In het licht van de huidige gebeurtenissen als gevolg van het coronavirus, hebben verschillende internetdiensten een increased belasting ontvangen. Bijvoorbeeld, <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\">een van de winkelketens in het Verenigd Koninkrijk heeft simpelweg de website voor online bestellingen stopgezet<\/a><\/noindex>, omdat de capaciteit niet voldoende was. En het is niet altijd mogelijk om een server te versnellen door simpelweg krachtigere hardware toe te voegen, maar je moet wel de klantverzoeken verwerken (anders gaan ze naar de concurrentie).<\/p>\n<p><\/p>\n<p>In dit artikel geef ik een korte uiteenzetting van populaire praktijken die het mogelijk maken om een snelle en fouttolerante service te cre\u00ebren. Ik heb echter uit de mogelijke ontwikkelingsschema's alleen de geselecteerd die momenteel <strong>gemakkelijk te gebruiken zijn<\/strong>. Voor elk punt heeft u ofwel al bestaande bibliotheken, ofwel de mogelijkheid om dit probleem op te lossen via een cloudplatform.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<h1 id=\"gorizontalnoe-masshtabirovanie\">Horizontale schaalvergroting<\/h1>\n<p><\/p>\n<p>Het meest eenvoudige en algemeen bekende punt. In wezen zijn er twee vaak voorkomende manieren van load balancing \u2014 horizontale en verticale schaalvergroting. <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 het eerste geval<\/a><\/noindex> staat u de services toe om parallel te werken, waardoor de belasting tussen hen wordt verdeeld. <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 het tweede geval<\/a><\/noindex> bestelt u krachtigere servers of optimaliseert u de code.<\/p>\n<p><\/p>\n<p>Als voorbeeld neem ik een abstracte cloudopslag voor bestanden, dus een soortgelijke dienst als OwnCloud, OneDrive, enzovoort.<\/p>\n<p><\/p>\n<p>De standaardillustratie van een dergelijk schema staat hieronder, maar het laat alleen de complexiteit van het systeem zien. We moeten immers op de een of andere manier de services synchroniseren. Wat gebeurt er als een gebruiker een bestand vanaf een tablet heeft opgeslagen en het vervolgens vanaf een telefoon wil bekijken?<\/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 \/>\nHet verschil tussen de benaderingen: bij verticale schaalvergroting zijn we bereid om de prestaties van de knooppunten te verhogen, terwijl we bij horizontale schaalvergroting nieuwe knooppunten toevoegen om de belasting te verdelen.<\/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 een vrij belangrijk patroon, omdat het verschillende klanten in staat stelt niet alleen verbinding te maken met verschillende services, maar ook dezelfde event streams te ontvangen. De voordelen zijn niet zo voor de hand liggend voor een eenvoudige applicatie, maar het is van cruciaal belang (en eenvoudig) voor een zwaar belaste service. De essentie is: inkomende en uitgaande datastromen mogen elkaar niet overlappen. Dat betekent dat u geen verzoek kunt sturen en een antwoord kunt verwachten; in plaats daarvan stuurt u een verzoek naar service A, maar ontvangt u het antwoord in service B.<\/p>\n<p><\/p>\n<p>De eerste bonus van deze aanpak is de mogelijkheid om de verbinding (in de brede zin van het woord) te verbreken tijdens een langlopende aanvraag. Laten we als voorbeeld een meer of minder standaard volgorde nemen:<\/p>\n<p><\/p>\n<ol>\n<li>De klant heeft een verzoek naar de server verzonden.<\/li>\n<li>De server is begonnen met een langdurige verwerking.<\/li>\n<li>De server heeft de klant een resultaat gegeven.<\/li>\n<\/ol>\n<p><\/p>\n<p>Stel je voor dat er in punt 2 een verbindingsonderbreking heeft plaatsgevonden (of het netwerk is opnieuw verbonden, of de gebruiker is naar een andere pagina gegaan en heeft de verbinding verbroken). In dit geval zal het voor de server moeilijk zijn om de gebruiker een antwoord te sturen met de informatie over wat precies is verwerkt. Bij het toepassen van CQRS zal de volgorde iets anders zijn:<\/p>\n<p><\/p>\n<ol>\n<li>De klant heeft zich ingeschreven voor updates.<\/li>\n<li>De klant heeft een verzoek naar de server verzonden.<\/li>\n<li>De server antwoordde met 'verzoek ontvangen'.<\/li>\n<li>De server heeft het resultaat via het kanaal van punt \u20181\u2019 verzonden.<\/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>Zoals je kunt zien, is het schema iets complexer. Bovendien ontbreekt de intu\u00eftieve aanvraag-reactie aanpak hier. Maar zoals te zien is, zal een verbindingsonderbreking tijdens de verwerking van een aanvraag niet leiden tot een fout. Bovendien, als de gebruiker daadwerkelijk vanaf meerdere apparaten is verbonden met de service (bijvoorbeeld vanaf een mobiele telefoon en een tablet), is het mogelijk om ervoor te zorgen dat het antwoord op beide apparaten aankomt.<\/p>\n<p><\/p>\n<p>Het interessante is dat de code voor het verwerken van inkomende berichten hetzelfde wordt (niet voor 100%) zowel voor gebeurtenissen die door de klant zelf zijn be\u00efnvloed als voor andere gebeurtenissen, inclusief die van andere klanten.<\/p>\n<p><\/p>\n<p>In de praktijk krijgen we echter extra bonussen doordat de eenrichtingsstroom in functionele stijl kan worden verwerkt (met RX en soortgelijke methoden). Dit is al een serieuze pluspunt, aangezien de applicatie in wezen volledig reactief kan worden gemaakt, met inachtneming van een functionele aanpak. Voor zware programma's kan dit aanzienlijk middelen besparen voor ontwikkeling en ondersteuning.<\/p>\n<p><\/p>\n<p>Als we deze aanpak combineren met horizontale schaalbaarheid, krijgen we als bonus de mogelijkheid om verzoeken naar \u00e9\u00e9n server te sturen en antwoorden van een andere te ontvangen. Op deze manier kan de klant zelf de service kiezen die het beste bij hem past, terwijl het systeem intern de gebeurtenissen correct kan verwerken.<\/p>\n<p><\/p>\n<h1 id=\"event-sourcing\">Event Sourcing<\/h1>\n<p><\/p>\n<p>Zoals u weet, is een van de belangrijkste kenmerken van een verdeeld systeem het ontbreken van een gemeenschappelijke tijd en een gemeenschappelijke kritieke sectie. Voor \u00e9\u00e9n proces kunt u synchronisatie toepassen (op dezelfde mutexen), waarbij u zeker weet dat niemand anders deze code uitvoert. Voor een verdeeld systeem is dit echter gevaarlijk, omdat het overhead met zich meebrengt en bovendien de schoonheid van schaalvergroting verloren gaat \u2014 toch zullen alle componenten op elkaar moeten wachten.<\/p>\n<p><\/p>\n<p>Hieruit kunnen we een belangrijke conclusie trekken \u2014 een snelle verdeelde systeem kan niet worden gesynchroniseerd, omdat dit de prestaties zou verminderen. Aan de andere kant is er vaak behoefte aan bepaalde consistentie tussen componenten. En daarvoor kan de volgende benadering worden gebruikt. <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">eventual consistency<\/a><\/noindex>, waar wordt gegarandeerd dat, in de afwezigheid van gegevenswijzigingen gedurende een bepaalde periode na de laatste update (\u2018uiteindelijk\u2019), alle verzoeken de laatst bijgewerkte waarde zullen teruggeven.<\/p>\n<p><\/p>\n<p>Het is belangrijk te begrijpen dat voor klassieke databases vaak wordt toegepast. <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Consistency_model#Strict_consistency\">strikte consistentie<\/a><\/noindex>, waarbij elke knoop dezelfde informatie bezit (dit wordt vaak bereikt wanneer een transactie als bevestigd wordt beschouwd pas na een antwoord van de tweede server). Hier zijn er enkele verslappingen vanwege isolatieniveaus, maar de essentie blijft dezelfde \u2014 u kunt leven in een volledig consistente wereld.<\/p>\n<p><\/p>\n<p>Laten we echter terugkeren naar de oorspronkelijke taak. Als een deel van het systeem kan worden opgebouwd met <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Eventual_consistency\">eventual consistency<\/a><\/noindex>, kan de volgende schema worden gebouwd.<\/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>Belangrijke kenmerken van deze benadering:<\/p>\n<p><\/p>\n<ul>\n<li>Elk binnenkomend verzoek wordt in een enkele wachtrij geplaatst.<\/li>\n<li>Bij de verwerking van een verzoek kan de service ook taken in andere wachtrijen plaatsen.<\/li>\n<li>Elk binnenkomend evenement heeft een identificator (die nodig is voor deduplicatie).<\/li>\n<li>De wachtrij werkt ideologisch volgens het \"append only\"-schema. Elementen kunnen niet worden verwijderd of verplaatst.<\/li>\n<li>De wachtrij werkt volgens het FIFO-principe (excuses voor de tautologie). Als parallelle verwerking noodzakelijk is, moeten objecten in een van de fasen naar verschillende wachtrijen worden overgebracht.<\/li>\n<\/ul>\n<p><\/p>\n<p>Ter herinnering, we beschouwen de situatie van een online bestandsopslag. In dit geval zal het systeem er ongeveer als volgt uitzien:<\/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>Het is belangrijk dat de diensten op het diagram niet noodzakelijk afzonderlijke servers betekenen. Zelfs het proces kan hetzelfde zijn. Belangrijker is: ideologisch zijn deze zaken zo gescheiden dat horizontale schaalvergroting gemakkelijk kan worden toegepast.<\/p>\n<p><\/p>\n<p>Voor twee gebruikers zou het schema er als volgt uitzien (diensten die voor verschillende gebruikers bestemd zijn, zijn in verschillende kleuren gemarkeerd):<\/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>Voordelen van een dergelijke combinatie:<\/p>\n<p><\/p>\n<ul>\n<li>De informatieverwerkingsdiensten zijn gescheiden. De wachtrijen zijn ook gescheiden. Als we de doorvoercapaciteit van het systeem willen verhogen, hoeven we alleen maar meer diensten op meer servers te starten.<\/li>\n<li>Wanneer we informatie van de gebruiker ontvangen, hoeven we niet te wachten op de volledige opslag van gegevens. Integendeel, we kunnen eenvoudig 'ok' antwoorden en vervolgens geleidelijk aan met het werk beginnen. Tegelijkertijd verzacht de wachtrij pieken, aangezien het toevoegen van een nieuw object snel gebeurt en de gebruiker niet hoeft te wachten op een volledige doorloop van de cyclus.<\/li>\n<li>Als voorbeeld heb ik een deduplicatieservice toegevoegd, die probeert identieke bestanden samen te voegen. Als deze lang werkt voor 1% van de gevallen, zal de klant dit praktisch niet opmerken (zie hierboven), wat een groot voordeel is, aangezien van ons niet een honderd procent snelheid en betrouwbaarheid wordt vereist.<\/li>\n<\/ul>\n<p><\/p>\n<p>Echter, de nadelen zijn ook onmiddellijk zichtbaar:<\/p>\n<p><\/p>\n<ul>\n<li>Onze systeem heeft strikte consistentie verloren. Dit betekent dat als je je bijvoorbeeld aanmeldt voor verschillende diensten, je theoretisch verschillende toestanden kunt ontvangen (aangezien een van de diensten mogelijk het bericht van de interne wachtrij niet op tijd ontvangt). Als een ander gevolg heeft het systeem nu geen gezamenlijke tijd. Dit betekent dat je bijvoorbeeld alle gebeurtenissen niet eenvoudig kunt sorteren op basis van tijd van binnenkomst, omdat de klokken tussen servers mogelijk niet gesynchroniseerd zijn (bovendien is dezelfde tijd op twee servers een utopie).<\/li>\n<li>Geen enkele gebeurtenis kan nu eenvoudig worden teruggedraaid (zoals met een database mogelijk zou zijn). In plaats daarvan moet er een nieuwe gebeurtenis worden toegevoegd \u2014 <noindex><a rel=\"nofollow\" href=\"https:\/\/stackoverflow.com\/questions\/49451237\/compensating-events-on-cqrs-es-architecture\">compensatie gebeurtenis<\/a><\/noindex>, die de laatste toestand naar de vereiste zal wijzigen. Als voorbeeld uit een vergelijkbaar gebied: zonder het herschrijven van de geschiedenis (wat in sommige gevallen slecht is) kan men in git geen commit terugdraaien, maar kan men wel een speciale <noindex><a rel=\"nofollow\" href=\"https:\/\/git-scm.com\/docs\/git-revert\">rollback commit<\/a><\/noindex>, wat in wezen gewoon de oude staat zal teruggeven. Echter, zowel de foutieve commit als de rollback zullen in de geschiedenis blijven.<\/li>\n<li>Het datamodel kan van release tot release veranderen, maar oude gebeurtenissen kunnen nu niet meer worden bijgewerkt naar de nieuwe standaard (aangezien gebeurtenissen in principe niet kunnen worden gewijzigd).<\/li>\n<\/ul>\n<p><\/p>\n<p>Zoals te zien is, past Event Sourcing uitstekend samen met CQRS. Sterker nog, het implementeren van een systeem met effici\u00ebnte en handige wachtrijen, maar zonder datastroomsegmentatie, is al een uitdaging op zich, want het vereist het toevoegen van synchronisatiepunten die het positieve effect van de wachtrijen tenietdoen. Door beide benaderingen tegelijkertijd toe te passen, moet de programmatuur enigszins worden aangepast. In ons geval, bij het uploaden van een bestand naar de server, komt er alleen een 'ok' als reactie, wat betekent dat de 'bestandsadditie-operatie is opgeslagen'. Formeel betekent dit niet dat de gegevens al op andere apparaten beschikbaar zijn (bijvoorbeeld, de deduplicatieservice kan de index opnieuw opbouwen). Echter, na enige tijd ontvangt de klant een notificatie als 'bestand X is opgeslagen'.<\/p>\n<p><\/p>\n<p>Als resultaat:<\/p>\n<p><\/p>\n<ul>\n<li>Het aantal bestandsstatussen neemt toe: in plaats van de klassieke 'bestand verzonden' krijgen we er twee: 'bestand in wachtrij op de server toegevoegd' en 'bestand opgeslagen in de opslag'. Laatstgenoemde betekent dat andere apparaten het bestand al kunnen beginnen te ontvangen (met de kanttekening dat wachtrijen met verschillende snelheden werken).<\/li>\n<li>Vanwege het feit dat informatie over verzending nu via verschillende kanalen binnenkomt, moeten we oplossingen bedenken om de status van de bestandverwerking te ontvangen. Hierdoor kan de client, in tegenstelling tot de klassieke request-response, tijdens de bestandsverwerking opnieuw worden opgestart, maar de status van de verwerking is correct. Dit punt werkt in wezen out-of-the-box. Hierdoor zijn we nu toleranter voor storingen.<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"sharding\">Sharding<\/h1>\n<p><\/p>\n<p>Zoals hierboven beschreven, ontbreekt in systemen met event sourcing strikte consistentie. Dit betekent dat we meerdere opslagplaatsen kunnen gebruiken zonder enige synchronisatie ertussen. Dichtbij onze taak kunnen we:<\/p>\n<p><\/p>\n<ul>\n<li>Bestanden indelen op type. Bijvoorbeeld, afbeeldingen\/video's kunnen worden gedecodeerd en er kan een effici\u00ebnter formaat worden gekozen.<\/li>\n<li>Accounts splits per country. Due to many laws, this may be required, however, this architecture scheme allows for such an option automatically.<\/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 means will not suffice here. Unfortunately, in this case, it is necessary to stop the queue, perform the migration, and then restart it. Generally, data cannot be transferred '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 own identifier (ideally, a non-decreasing one). Thus, we can add a field in the storage \u2014 the id of the last processed item.<\/li>\n<li>We duplicate the queue so that all events can be processed for several independent storages (the first is the one that is already storing data, and the second is new, while still empty). The second queue is naturally not being processed yet.<\/li>\n<li>We start the second queue (that is, we begin replaying events).<\/li>\n<li>When the new queue is relatively empty (i.e., the average time difference between adding an item and retrieving it is acceptable), we can start switching readers to the new storage.<\/li>\n<\/ul>\n<p><\/p>\n<p>As you can see, our system has never had strict consistency. There is only eventual consistency, meaning that events are guaranteed to be processed in the same order (however, possibly with different delays). And, using this, we can comparatively easily transfer data without stopping the system to the other side of the globe.<\/p>\n<p><\/p>\n<p>Thus, continuing our example of an online storage for files, such an architecture already gives us a number of bonuses:<\/p>\n<p><\/p>\n<ul>\n<li>We can move objects closer to users, and dynamically. This can improve service quality. <\/li>\n<li>We can store part of the data within companies. For example, enterprise users often require their data to be stored in controlled data centers (to prevent data leaks). With sharding, we can easily support this. The task is further simplified if the customer has a compatible cloud (for instance, <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>Het belangrijkste is dat we het niet hoeven te doen. Voorlopig zou \u00e9\u00e9n opslag voor alle accounts ons prima passen (om sneller aan de slag te gaan). En het sleutelkenmerk van dit systeem is dat, hoewel het uitbreidbaar is, het in het begin vrij eenvoudig is. Je hoeft niet meteen code te schrijven die werkt met een miljoen afzonderlijke onafhankelijke wachtrijen, enzovoorts. Als dat nodig is, kan dat in de toekomst.<\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"static-content-hosting\">Static Content Hosting<\/h1>\n<p><\/p>\n<p>Dit punt lijkt misschien heel vanzelfsprekend, maar het is nog steeds noodzakelijk voor een min of meer standaard zwaar beladen applicatie. Het idee is simpel: alle statische content wordt niet vanaf dezelfde server gedistribueerd als de applicatie, maar vanaf speciale servers die hiervoor zijn ingericht. Dit betekent dat deze operaties sneller verlopen (bijvoorbeeld, nginx levert bestanden sneller en kosteneffectiever dan een Java-server). Bovendien maakt de architectuur van een CDN (<noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Content_delivery_network\">Content Delivery Network<\/a><\/noindex>) het mogelijk om onze bestanden dichter bij eindgebruikers te plaatsen, wat de gebruikservaring met de service verbetert.<\/p>\n<p><\/p>\n<p>Het eenvoudigste en meest standaard voorbeeld van statische content is een set scripts en afbeeldingen voor een website. Dit is vrij straightforward \u2014 ze zijn van tevoren bekend, vervolgens wordt het archief naar de CDN-servers ge\u00fcpload, vanwaar het aan eindgebruikers wordt geleverd.<\/p>\n<p><\/p>\n<p>Echter, in de praktijk kan voor statische content een aanpak worden gebruikt die lijkt op een lambda-architectuur. Laten we terugkeren naar onze taak (een online bestandopslag), waarin we bestanden aan gebruikers moeten leveren. De eenvoudigste directe oplossing is om een service te maken die voor elk gebruikersverzoek alle noodzakelijke controles uitvoert (autorisatie, enzovoort), en vervolgens het bestand direct van onze opslag downloadt. Het belangrijkste nadeel van deze aanpak is dat de statische content (en een bestand met een bepaalde revisie is in wezen statische content) wordt geleverd door dezelfde server die de bedrijfslogica bevat. In plaats daarvan kan het volgende schema worden gemaakt:<\/p>\n<p><\/p>\n<ul>\n<li>De server geeft een URL voor het downloaden. Deze kan de vorm hebben van file_id + key, waarbij key een kleine digitale handtekening is die toegang geeft tot de resource voor de komende 24 uur.<\/li>\n<li>Een simpele nginx verzorgt het bestand met de volgende opties:\n<ul>\n<li>Content caching. Since this service may be on a separate server, we have left room for future potential to store all recently downloaded files on disk.<\/li>\n<li>Key check at the moment of connection creation.<\/li>\n<\/ul>\n<\/li>\n<li>Optionally: streaming content processing. For example, if we compress all files in the service, we can perform decompression directly within this module. Consequently, IO operations are done where they belong. A Java archiver may consume a lot of excess memory, but rewriting the service with business logic in conditional Rust\/C++ could also prove inefficient. In our case, different processes (or even services) are utilized, making it quite effective to separate business logic from 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>Such a scheme doesn\u2019t closely resemble serving static content (since we are not offloading the entire static package somewhere), yet in reality, this approach indeed specializes in delivering immutable data. Moreover, this scheme can be generalized to other cases where content is not merely static but can be represented as a set of immutable and non-deletable blocks (though they can be added).<\/p>\n<p><\/p>\n<p>Als een ander voorbeeld (ter bevestiging): als je met Jenkins\/TeamCity hebt gewerkt, dan weet je dat beide oplossingen zijn geschreven in Java. Beiden zijn Java-processen die zowel de bouworkestratie als het contentbeheer verzorgen. In het bijzonder hebben ze beide taken zoals \"stuur een bestand\/map vanaf de server\". Bijvoorbeeld: het vrijgeven van artefacten, het overdragen van de broncode (wanneer de agent niet de code rechtstreeks uit het repository downloadt, maar de server dit doet), toegang tot de logs. Al deze taken verschillen in de IO-belasting. Dit betekent dat de server, die verantwoordelijk is voor complexe bedrijfslogica, tegelijkertijd in staat moet zijn om grote datastromen effici\u00ebnt te verwerken. En het interessante is dat een dergelijke operatie aan dezelfde nginx kan worden gedelegeerd op exact dezelfde manier (behalve dat er een datakey aan de aanvraag moet worden toegevoegd).<\/p>\n<p><\/p>\n<p>However, if we return to our system, the result is 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>Zoals te zien is, is het systeem radicaal ingewikkelder geworden. Het is nu niet langer gewoon een miniproces dat bestanden lokaal opslaat. Nu zijn er al meer geavanceerde ondersteuning en versiebeheer van API\u2019s nodig, enzovoort. Daarom is het, nadat alle diagrammen zijn getekend, het beste om gedetailleerd te beoordelen of de uitbreidbaarheid van dit soort kosten gerechtvaardigd is. Maar als je de mogelijkheid wilt hebben om het systeem uit te breiden (ook om met een nog groter aantal gebruikers te werken), dan zul je voor dergelijke oplossingen moeten kiezen. Het resultaat is dat het architecturaal systeem klaar is om de belasting te verhogen (bijna elke component kan worden gekloond voor horizontale schaalvergroting). Het systeem kan worden bijgewerkt zonder dat het stilgelegd hoeft te worden (alleen sommige bewerkingen zullen een beetje vertragen).<\/p>\n<p><\/p>\n<p>Zoals ik aan het begin al zei, krijgt een aantal internetdiensten nu een verhoogde belasting. En sommige van hen zijn gewoon gestopt met goed functioneren. In feite heeft het systeem gefaald op het moment dat het bedrijf net geld had moeten verdienen. In wezen zei het systeem: \"ga maar naar de concurrenten\" in plaats van uitgestelde levering of het aanbieden aan klanten om \"de levering voor de komende maanden te plannen\". Dit is wat de prijs van lage prestaties is: verliezen zullen zich precies op het moment voordoen dat de winst het hoogst zou zijn.<\/p>\n<p><\/p>\n<h1 id=\"zaklyuchenie\">Conclusie<\/h1>\n<p><\/p>\n<p>Al deze benaderingen waren eerder al bekend. VK maakt al lange tijd gebruik van het idee van Static Content Hosting voor het leveren van afbeeldingen. Veel online games gebruiken de Sharding-schema om spelers over regio's te verdelen of om spelsituaties te scheiden (als de wereld \u00e9\u00e9n geheel is). De Event Sourcing-aanpak wordt actief gebruikt in e-mail. De meeste handelsapplicaties, waar continu gegevens binnenkomen, zijn eigenlijk gebouwd op de CQRS-aanpak om de binnenkomende gegevens te kunnen filteren. Horizontale schaalvergroting wordt ook al geruime tijd in veel diensten toegepast.<\/p>\n<p><\/p>\n<p>Maar het belangrijkste is dat al deze patronen nu heel gemakkelijk toepasbaar zijn in moderne applicaties (tenzij ze uiteraard ongepast zijn). Cloud-omgevingen bieden sharding en horizontale schaalbaarheid direct aan, wat veel eenvoudiger is dan het zelf bestellen van verschillende dedicated servers in verschillende datacenters. CQRS is veel gemakkelijker geworden, onder andere door de ontwikkeling van bibliotheken zoals RX. Tien jaar geleden zou een zeldzame website dat aankunnen. Event sourcing is ook ongelooflijk eenvoudig in te richten dankzij de beschikbare containers met Apache Kafka. Tien jaar geleden zou dit als vernieuwend worden beschouwd, terwijl het nu heel gewoon is. Evenzo geldt dit voor static content hosting: door gebruiksvriendelijkere technologie\u00ebn (waaronder uitgebreide documentatie en een groot aantal antwoorden) is deze aanpak nog eenvoudiger geworden.<\/p>\n<p><\/p>\n<p>Als conclusie is de implementatie van een aantal behoorlijk complexe architecturale patronen nu veel eenvoudiger geworden, en dat betekent dat het goed is om er van tevoren naar te kijken. Als in een applicatie van tien jaar geleden een van de bovenstaande oplossingen werd afgewezen vanwege de hoge kosten van implementatie en exploitatie, kan nu in een nieuwe applicatie, of na een refactoring, een service worden gebouwd die architectonisch zowel uitbreidbaar (in termen van prestaties) als klaar voor nieuwe klantbehoeften (bijvoorbeeld voor de lokalisatie van persoonlijke gegevens) zal zijn.<\/p>\n<p><\/p>\n<p>En het belangrijkste: gebruik deze benaderingen alsjeblieft niet als je een eenvoudige applicatie hebt. Ja, ze zijn mooi en interessant, maar voor een website met een piekbezoek van 100 mensen kan je vaak volstaan met een klassieke monolith (al kan je intern alles opdelen in modules, etc.).<\/p>\n<p>Bron: <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.3 - 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\/nl\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"nl_NL\" \/>\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\/nl\/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\udd47Handige architecturale patronen | ProHoster","description":"Hallo, Habr! In het licht van de huidige gebeurtenissen door het coronavirus hebben sommige internetdiensten een toegenomen belasting gekregen.","canonical_url":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/udobnye-arhitekturnye-patterny","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"nl_NL","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\/nl\/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\/nl\/wp-json\/wp\/v2\/posts\/80035","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/comments?post=80035"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/80035\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media\/80036"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media?parent=80035"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/categories?post=80035"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/tags?post=80035"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}