Des modĂšles architecturaux pratiques

HĂ© Habr !

À la lumiĂšre des Ă©vĂ©nements actuels liĂ©s au coronavirus, un certain nombre de services Internet ont commencĂ© Ă  recevoir une charge accrue. Par exemple, L’une des chaĂźnes de vente au dĂ©tail britanniques a tout simplement fermĂ© son site de commande en ligne., parce qu'il n'y avait pas assez de capacitĂ©. Et il n’est pas toujours possible d’accĂ©lĂ©rer un serveur en ajoutant simplement du matĂ©riel plus puissant, mais les demandes des clients doivent ĂȘtre traitĂ©es (sinon elles iront aux concurrents).

Dans cet article, je parlerai briĂšvement des pratiques populaires qui vous permettront de crĂ©er un service rapide et tolĂ©rant aux pannes. Cependant, parmi les schĂ©mas de dĂ©veloppement possibles, j'ai sĂ©lectionnĂ© uniquement ceux qui sont actuellement facile Ă  utiliser. Pour chaque Ă©lĂ©ment, soit vous disposez de bibliothĂšques prĂȘtes Ă  l'emploi, soit vous avez la possibilitĂ© de rĂ©soudre le problĂšme Ă  l'aide d'une plate-forme cloud.

Mise à l'échelle horizontale

Le point le plus simple et le plus connu. Classiquement, les deux schémas de répartition de charge les plus courants sont la mise à l'échelle horizontale et verticale. Dans le premier cas vous autorisez les services à s'exécuter en parallÚle, répartissant ainsi la charge entre eux. Dans la seconde vous commandez des serveurs plus puissants ou optimisez le code.

Par exemple, je prendrai le stockage de fichiers cloud abstrait, c'est-Ă -dire un analogue de OwnCloud, OneDrive, etc.

Une image standard d'un tel circuit est prĂ©sentĂ©e ci-dessous, mais elle ne fait que dĂ©montrer la complexitĂ© du systĂšme. AprĂšs tout, nous devons d'une maniĂšre ou d'une autre synchroniser les services. Que se passe-t-il si l'utilisateur enregistre un fichier depuis la tablette et souhaite ensuite le visualiser depuis le tĂ©lĂ©phone ?

Des modĂšles architecturaux pratiques
La diffĂ©rence entre les approches : en mise Ă  l'Ă©chelle verticale, nous sommes prĂȘts Ă  augmenter la puissance des nƓuds, et en mise Ă  l'Ă©chelle horizontale, nous sommes prĂȘts Ă  ajouter de nouveaux nƓuds pour rĂ©partir la charge.

CQRS

SĂ©grĂ©gation de responsabilitĂ© de requĂȘte de commande Un modĂšle assez important, puisqu'il permet Ă  diffĂ©rents clients non seulement de se connecter Ă  diffĂ©rents services, mais Ă©galement de recevoir les mĂȘmes flux d'Ă©vĂ©nements. Ses avantages ne sont pas si Ă©vidents pour une application simple, mais ils sont extrĂȘmement importants (et simples) pour un service trĂšs chargĂ©. Son essence : les flux de donnĂ©es entrants et sortants ne doivent pas se croiser. Autrement dit, vous ne pouvez pas envoyer une requĂȘte et attendre une rĂ©ponse ; Ă  la place, vous envoyez une requĂȘte au service A, mais recevez une rĂ©ponse du service B.

Le premier bonus de cette approche est la possibilitĂ© de rompre la connexion (au sens large du terme) tout en exĂ©cutant une requĂȘte longue. Par exemple, prenons une sĂ©quence plus ou moins standard :

  1. Le client a envoyĂ© une requĂȘte au serveur.
  2. Le serveur a démarré un long temps de traitement.
  3. Le serveur a répondu au client avec le résultat.

Imaginons qu'au point 2 la connexion ait Ă©tĂ© interrompue (ou le rĂ©seau s'est reconnectĂ©, ou l'utilisateur est allĂ© sur une autre page, rompant la connexion). Dans ce cas, il sera difficile pour le serveur d'envoyer une rĂ©ponse Ă  l'utilisateur contenant des informations sur ce qui a Ă©tĂ© traitĂ© exactement. En utilisant CQRS, la sĂ©quence sera lĂ©gĂšrement diffĂ©rente :

  1. Le client s'est abonné aux mises à jour.
  2. Le client a envoyĂ© une requĂȘte au serveur.
  3. Le serveur a rĂ©pondu « demande acceptĂ©e Â».
  4. Le serveur a rĂ©pondu avec le rĂ©sultat via le canal du point « 1 Â».

Des modĂšles architecturaux pratiques

Comme vous pouvez le constater, le schĂ©ma est un peu plus compliquĂ©. De plus, l’approche intuitive demande-rĂ©ponse fait dĂ©faut ici. Cependant, comme vous pouvez le constater, une rupture de connexion lors du traitement d’une requĂȘte n’entraĂźnera pas d’erreur. De plus, si en fait l'utilisateur est connectĂ© au service depuis plusieurs appareils (par exemple, depuis un tĂ©lĂ©phone mobile et depuis une tablette), vous pouvez vous assurer que la rĂ©ponse arrive sur les deux appareils.

Fait intĂ©ressant, le code de traitement des messages entrants devient le mĂȘme (pas Ă  100 %) Ă  la fois pour les Ă©vĂ©nements influencĂ©s par le client lui-mĂȘme et pour d'autres Ă©vĂ©nements, y compris ceux d'autres clients.

Cependant, en rĂ©alitĂ©, nous obtenons un bonus supplĂ©mentaire dĂ» au fait que le flux unidirectionnel peut ĂȘtre gĂ©rĂ© de maniĂšre fonctionnelle (en utilisant RX et similaire). Et c'est dĂ©jĂ  un sĂ©rieux plus, puisqu'en substance l'application peut ĂȘtre rendue totalement rĂ©active, et Ă©galement en utilisant une approche fonctionnelle. Pour les gros programmes, cela peut Ă©conomiser considĂ©rablement les ressources de dĂ©veloppement et de support.

Si nous combinons cette approche avec une mise Ă  l'Ă©chelle horizontale, nous obtenons en prime la possibilitĂ© d'envoyer des requĂȘtes Ă  un serveur et de recevoir des rĂ©ponses d'un autre. Ainsi, le client peut choisir le service qui lui convient et le systĂšme interne sera toujours capable de traiter correctement les Ă©vĂ©nements.

Sourcing événementiel

Comme vous le savez, l'une des principales caractĂ©ristiques d'un systĂšme distribuĂ© est l'absence d'heure commune, de section critique commune. Pour un processus, vous pouvez effectuer une synchronisation (sur les mĂȘmes mutex), au sein de laquelle vous ĂȘtes sĂ»r que personne d'autre n'exĂ©cute ce code. Cependant, cela est dangereux pour un systĂšme distribuĂ©, car cela nĂ©cessitera une surcharge et tuera Ă©galement toute la beautĂ© de la mise Ă  l'Ă©chelle - tous les composants en attendront toujours une.

De lĂ , nous obtenons un fait important : un systĂšme distribuĂ© rapide ne peut pas ĂȘtre synchronisĂ©, car nous rĂ©duirions alors les performances. En revanche, on a souvent besoin d’une certaine cohĂ©rence entre les composants. Et pour cela, vous pouvez utiliser l'approche avec cohĂ©rence Ă©ventuelle, oĂč il est garanti que s'il n'y a pas de modification des donnĂ©es pendant un certain temps aprĂšs la derniĂšre mise Ă  jour (« Ă©ventuellement »), toutes les requĂȘtes renverront la derniĂšre valeur mise Ă  jour.

Il est important de comprendre que pour les bases de donnĂ©es classiques, il est assez souvent utilisĂ© forte cohĂ©rence, oĂč chaque nƓud a les mĂȘmes informations (ceci est souvent rĂ©alisĂ© dans le cas oĂč la transaction est considĂ©rĂ©e comme Ă©tablie seulement aprĂšs la rĂ©ponse du deuxiĂšme serveur). Il y a quelques assouplissements ici en raison des niveaux d'isolement, mais l'idĂ©e gĂ©nĂ©rale reste la mĂȘme : vous pouvez vivre dans un monde complĂštement harmonisĂ©.

Cependant, revenons Ă  la tĂąche initiale. Si une partie du systĂšme peut ĂȘtre construite avec cohĂ©rence Ă©ventuelle, alors nous pouvons construire le diagramme suivant.

Des modĂšles architecturaux pratiques

CaractĂ©ristiques importantes de cette approche :

  • Chaque demande entrante est placĂ©e dans une file d'attente.
  • Lors du traitement d'une demande, le service peut Ă©galement placer des tĂąches dans d'autres files d'attente.
  • Chaque Ă©vĂ©nement entrant possĂšde un identifiant (nĂ©cessaire Ă  la dĂ©duplication).
  • La file d'attente fonctionne idĂ©ologiquement selon le schĂ©ma « ajouter uniquement ». Vous ne pouvez pas en supprimer des Ă©lĂ©ments ni les rĂ©organiser.
  • La file d'attente fonctionne selon le schĂ©ma FIFO (dĂ©solĂ© pour la tautologie). Si vous devez effectuer une exĂ©cution parallĂšle, vous devez Ă  un moment donnĂ© dĂ©placer les objets vers diffĂ©rentes files d'attente.

Je vous rappelle que nous Ă©tudions le cas du stockage de fichiers en ligne. Dans ce cas, le systĂšme ressemblera Ă  ceci :

Des modĂšles architecturaux pratiques

Il est important que les services prĂ©sentĂ©s dans le diagramme ne dĂ©signent pas nĂ©cessairement un serveur distinct. MĂȘme le processus peut ĂȘtre le mĂȘme. Une autre chose est importante : idĂ©ologiquement, ces Ă©lĂ©ments sont sĂ©parĂ©s de telle maniĂšre qu’une Ă©chelle horizontale peut ĂȘtre facilement appliquĂ©e.

Et pour deux utilisateurs le schéma ressemblera à ceci (les services destinés à différents utilisateurs sont indiqués dans des couleurs différentes) :

Des modĂšles architecturaux pratiques

Bonus d'une telle combinaison :

  • Les services de traitement de l'information sont sĂ©parĂ©s. Les files d'attente sont Ă©galement sĂ©parĂ©es. Si nous devons augmenter le dĂ©bit du systĂšme, il nous suffit alors de lancer davantage de services sur davantage de serveurs.
  • Lorsque nous recevons des informations d'un utilisateur, nous n'avons pas besoin d'attendre que les donnĂ©es soient complĂštement enregistrĂ©es. Au contraire, il suffit de rĂ©pondre « ok » et de commencer progressivement Ă  travailler. Dans le mĂȘme temps, la file d'attente attĂ©nue les pics, car l'ajout d'un nouvel objet se produit rapidement et l'utilisateur n'a pas Ă  attendre un parcours complet de tout le cycle.
  • A titre d'exemple, j'ai ajoutĂ© un service de dĂ©duplication qui tente de fusionner des fichiers identiques. Si cela fonctionne longtemps dans 1% des cas, le client ne s'en apercevra pratiquement pas (voir ci-dessus), ce qui est un gros plus, puisqu'on n'est plus obligĂ© d'ĂȘtre rapide et fiable Ă  XNUMX%.

Cependant, les inconvénients sont immédiatement visibles :

  • Notre systĂšme a perdu sa stricte cohĂ©rence. Cela signifie que si, par exemple, vous vous abonnez Ă  diffĂ©rents services, vous pouvez thĂ©oriquement obtenir un Ă©tat diffĂ©rent (puisque l'un des services peut ne pas avoir le temps de recevoir une notification de la file d'attente interne). Autre consĂ©quence : le systĂšme n'a plus d'heure commune. C'est-Ă -dire qu'il est impossible, par exemple, de trier tous les Ă©vĂ©nements simplement par heure d'arrivĂ©e, puisque les horloges entre serveurs peuvent ne pas ĂȘtre synchrones (d'ailleurs, la mĂȘme heure sur deux serveurs est une utopie).
  • Aucun Ă©vĂ©nement ne peut dĂ©sormais ĂȘtre simplement annulĂ© (comme cela pourrait ĂȘtre le cas avec une base de donnĂ©es). Au lieu de cela, vous devez ajouter un nouvel Ă©vĂ©nement - Ă©vĂ©nement de compensation, ce qui changera le dernier Ă©tat en celui requis. A titre d'exemple dans un domaine similaire : sans réécrire l'historique (ce qui est mauvais dans certains cas), vous ne pouvez pas annuler un commit dans git, mais vous pouvez crĂ©er un commit spĂ©cial. validation de restauration, qui renvoie essentiellement l'ancien Ă©tat. Cependant, la validation erronĂ©e et la restauration resteront dans l’historique.
  • Le schĂ©ma des donnĂ©es peut changer d'une version Ă  l'autre, mais les anciens Ă©vĂ©nements ne pourront plus ĂȘtre mis Ă  jour vers le nouveau standard (puisque les Ă©vĂ©nements ne peuvent en principe pas ĂȘtre modifiĂ©s).

Comme vous pouvez le constater, Event Sourcing fonctionne bien avec CQRS. De plus, mettre en Ɠuvre un systĂšme avec des files d'attente efficaces et pratiques, mais sans sĂ©parer les flux de donnĂ©es, est dĂ©jĂ  difficile en soi, car il faudra ajouter des points de synchronisation qui neutraliseront tout l'effet positif des files d'attente. En appliquant les deux approches Ă  la fois, il est nĂ©cessaire d'ajuster lĂ©gĂšrement le code du programme. Dans notre cas, lors de l'envoi d'un fichier au serveur, la rĂ©ponse arrive uniquement « ok », ce qui signifie uniquement que « l'opĂ©ration d'ajout du fichier a Ă©tĂ© enregistrĂ©e ». Formellement, cela ne signifie pas que les donnĂ©es sont dĂ©jĂ  disponibles sur d'autres appareils (par exemple, le service de dĂ©duplication peut reconstruire l'index). Cependant, aprĂšs un certain temps, le client recevra une notification du style « le fichier X a Ă©tĂ© enregistrĂ© Â».

Par conséquent:

  • Le nombre de statuts d'envoi de fichiers augmente : au lieu du classique « fichier envoyĂ© », nous en obtenons deux : « le fichier a Ă©tĂ© ajoutĂ© Ă  la file d'attente sur le serveur » et « le fichier a Ă©tĂ© enregistrĂ© en stockage ». Ce dernier signifie que d'autres appareils peuvent dĂ©jĂ  commencer Ă  recevoir le fichier (en tenant compte du fait que les files d'attente fonctionnent Ă  des vitesses diffĂ©rentes).
  • Étant donnĂ© que les informations de soumission transitent dĂ©sormais par diffĂ©rents canaux, nous devons trouver des solutions pour connaĂźtre l'Ă©tat de traitement du dossier. ConsĂ©quence de ceci : contrairement Ă  la requĂȘte-rĂ©ponse classique, le client peut ĂȘtre redĂ©marrĂ© pendant le traitement du fichier, mais le statut de ce traitement lui-mĂȘme sera correct. De plus, cet article fonctionne essentiellement immĂ©diatement. ConsĂ©quence : nous sommes dĂ©sormais plus tolĂ©rants Ă  l’égard des Ă©checs.

Sharding

Comme dĂ©crit ci-dessus, les systĂšmes de sourcing d’évĂ©nements manquent de cohĂ©rence stricte. Cela signifie que nous pouvons utiliser plusieurs stockages sans aucune synchronisation entre eux. En abordant notre problĂšme, nous pouvons :

  • SĂ©parez les fichiers par type. Par exemple, les images/vidĂ©os peuvent ĂȘtre dĂ©codĂ©es et un format plus efficace peut ĂȘtre sĂ©lectionnĂ©.
  • Comptes sĂ©parĂ©s par pays. En raison de nombreuses lois, cela peut ĂȘtre requis, mais ce schĂ©ma d'architecture offre automatiquement une telle opportunitĂ©.

Des modĂšles architecturaux pratiques

Si vous souhaitez transfĂ©rer des donnĂ©es d’un stockage Ă  un autre, alors les moyens standards ne suffisent plus. Malheureusement, dans ce cas, vous devez arrĂȘter la file d'attente, effectuer la migration, puis la dĂ©marrer. Dans le cas gĂ©nĂ©ral, les donnĂ©es ne peuvent pas ĂȘtre transfĂ©rĂ©es « Ă  la volĂ©e », cependant, si la file d'attente des Ă©vĂ©nements est entiĂšrement stockĂ©e et que vous disposez d'instantanĂ©s des Ă©tats de stockage prĂ©cĂ©dents, nous pouvons alors rejouer les Ă©vĂ©nements comme suit :

  • Dans Event Source, chaque Ă©vĂ©nement possĂšde son propre identifiant (idĂ©alement, non dĂ©croissant). Cela signifie que nous pouvons ajouter un champ au stockage - l'identifiant du dernier Ă©lĂ©ment traitĂ©.
  • Nous dupliquons la file d'attente afin que tous les Ă©vĂ©nements puissent ĂȘtre traitĂ©s pour plusieurs stockages indĂ©pendants (le premier est celui dans lequel les donnĂ©es sont dĂ©jĂ  stockĂ©es, et le second est nouveau, mais toujours vide). Bien entendu, la deuxiĂšme file d’attente n’est pas encore traitĂ©e.
  • Nous lançons la deuxiĂšme file d'attente (c'est-Ă -dire que nous commençons Ă  rejouer les Ă©vĂ©nements).
  • Lorsque la nouvelle file d'attente est relativement vide (c'est-Ă -dire que le dĂ©lai moyen entre l'ajout d'un Ă©lĂ©ment et sa rĂ©cupĂ©ration est acceptable), vous pouvez commencer Ă  faire basculer les lecteurs vers le nouveau stockage.

Comme vous pouvez le constater, nous n’avions pas, et n’avons toujours pas, une cohĂ©rence stricte dans notre systĂšme. Il n'y a qu'une cohĂ©rence Ă©ventuelle, c'est-Ă -dire une garantie que les Ă©vĂ©nements sont traitĂ©s dans le mĂȘme ordre (mais Ă©ventuellement avec des dĂ©lais diffĂ©rents). Et grĂące Ă  cela, nous pouvons transfĂ©rer des donnĂ©es relativement facilement sans arrĂȘter le systĂšme Ă  l’autre bout du monde.

Ainsi, poursuivant notre exemple sur le stockage de fichiers en ligne, une telle architecture nous apporte déjà un certain nombre de bonus :

  • Nous pouvons rapprocher les objets des utilisateurs de maniĂšre dynamique. De cette façon, vous pouvez amĂ©liorer la qualitĂ© du service.
  • Nous pouvons stocker certaines donnĂ©es au sein des entreprises. Par exemple, les utilisateurs d'entreprise exigent souvent que leurs donnĂ©es soient stockĂ©es dans des centres de donnĂ©es contrĂŽlĂ©s (pour Ă©viter les fuites de donnĂ©es). GrĂące au partitionnement, nous pouvons facilement prendre en charge cela. Et la tĂąche est encore plus facile si le client dispose d'un cloud compatible (par exemple, Azure auto-hĂ©bergĂ©).
  • Et le plus important c’est que nous n’ayons pas Ă  faire ça. AprĂšs tout, pour commencer, nous serions trĂšs satisfaits d'un seul stockage pour tous les comptes (pour commencer Ă  travailler rapidement). Et la principale caractĂ©ristique de ce systĂšme est que, bien qu’il soit extensible, il est assez simple au dĂ©but. Vous n’avez tout simplement pas besoin d’écrire immĂ©diatement du code qui fonctionne avec un million de files d’attente indĂ©pendantes distinctes, etc. Si nĂ©cessaire, cela pourra ĂȘtre fait Ă  l'avenir.

Hébergement de contenu statique

Ce point peut paraĂźtre assez Ă©vident, mais il reste nĂ©anmoins nĂ©cessaire pour une application chargĂ©e plus ou moins standard. Son essence est simple : tout le contenu statique n'est pas distribuĂ© Ă  partir du mĂȘme serveur sur lequel se trouve l'application, mais Ă  partir de serveurs spĂ©ciaux dĂ©diĂ©s spĂ©cifiquement Ă  cette tĂąche. En consĂ©quence, ces opĂ©rations sont effectuĂ©es plus rapidement (nginx conditionnel sert les fichiers plus rapidement et moins cher qu'un serveur Java). Plus l'architecture CDN (Content Delivery Network) nous permet de localiser nos fichiers plus prĂšs des utilisateurs finaux, ce qui a un effet positif sur la commoditĂ© de travailler avec le service.

L’exemple le plus simple et le plus standard de contenu statique est un ensemble de scripts et d’images pour un site Web. Tout est simple avec eux : ils sont connus Ă  l'avance, puis les archives sont tĂ©lĂ©chargĂ©es sur des serveurs CDN, d'oĂč elles sont distribuĂ©es aux utilisateurs finaux.

Cependant, en rĂ©alitĂ©, pour le contenu statique, vous pouvez utiliser une approche quelque peu similaire Ă  l'architecture lambda. Revenons Ă  notre tĂąche (stockage de fichiers en ligne), dans laquelle nous devons distribuer des fichiers aux utilisateurs. La solution la plus simple est de crĂ©er un service qui, pour chaque demande de l'utilisateur, effectue toutes les vĂ©rifications nĂ©cessaires (autorisation, etc.), puis tĂ©lĂ©charge le fichier directement depuis notre stockage. Le principal inconvĂ©nient de cette approche est que le contenu statique (et un fichier avec une certaine rĂ©vision est, en fait, un contenu statique) est distribuĂ© par le mĂȘme serveur qui contient la logique mĂ©tier. A la place, vous pouvez rĂ©aliser le schĂ©ma suivant :

  • Le serveur fournit une URL de tĂ©lĂ©chargement. Il peut ĂȘtre de la forme file_id + key, oĂč key est une mini-signature numĂ©rique qui donne le droit d'accĂ©der Ă  la ressource pour les prochaines XNUMX heures.
  • Le fichier est distribuĂ© par simple nginx avec les options suivantes :
    • Mise en cache du contenu. Ce service pouvant ĂȘtre localisĂ© sur un serveur sĂ©parĂ©, nous nous sommes laissĂ© une rĂ©serve pour l'avenir avec la possibilitĂ© de stocker sur disque tous les derniers fichiers tĂ©lĂ©chargĂ©s.
    • VĂ©rification de la clĂ© au moment de la crĂ©ation de la connexion
  • Facultatif : traitement du contenu en streaming. Par exemple, si nous compressons tous les fichiers du service, nous pouvons alors effectuer la dĂ©compression directement dans ce module. En consĂ©quence : les opĂ©rations d’E/S sont effectuĂ©es lĂ  oĂč elles doivent ĂȘtre. Un archiveur en Java allouera facilement beaucoup de mĂ©moire supplĂ©mentaire, mais réécrire un service avec une logique mĂ©tier dans des conditions Rust/C++ peut Ă©galement s'avĂ©rer inefficace. Dans notre cas, diffĂ©rents processus (voire services) sont utilisĂ©s, et nous pouvons donc sĂ©parer assez efficacement la logique mĂ©tier et les opĂ©rations d'E/S.

Des modĂšles architecturaux pratiques

Ce schĂ©ma n'est pas trĂšs similaire Ă  la distribution de contenu statique (puisque nous ne tĂ©lĂ©chargeons pas l'intĂ©gralitĂ© du package statique quelque part), mais en rĂ©alitĂ©, cette approche concerne prĂ©cisĂ©ment la distribution de donnĂ©es immuables. De plus, ce schĂ©ma peut ĂȘtre gĂ©nĂ©ralisĂ© Ă  d'autres cas oĂč le contenu n'est pas simplement statique, mais peut ĂȘtre reprĂ©sentĂ© comme un ensemble de blocs immuables et non supprimables (bien qu'ils puissent ĂȘtre ajoutĂ©s).

Comme autre exemple (Ă  titre de renforcement) : si vous avez travaillĂ© avec Jenkins/TeamCity, alors vous savez que les deux solutions sont Ă©crites en Java. Les deux sont un processus Java qui gĂšre Ă  la fois l’orchestration des builds et la gestion du contenu. En particulier, ils ont tous deux des tĂąches telles que « transfĂ©rer un fichier/dossier depuis le serveur ». A titre d'exemple : Ă©mission d'artefacts, transfert de code source (lorsque l'agent ne tĂ©lĂ©charge pas le code directement depuis le rĂ©fĂ©rentiel, mais que le serveur le fait pour lui), accĂšs aux logs. Toutes ces tĂąches diffĂšrent par leur charge d'E/S. Autrement dit, il s'avĂšre que le serveur responsable d'une logique mĂ©tier complexe doit en mĂȘme temps ĂȘtre capable de transmettre efficacement de gros flux de donnĂ©es Ă  travers lui-mĂȘme. Et ce qui est le plus intĂ©ressant, c'est qu'une telle opĂ©ration peut ĂȘtre dĂ©lĂ©guĂ©e au mĂȘme nginx selon exactement le mĂȘme schĂ©ma (sauf que la clĂ© de donnĂ©es doit ĂȘtre ajoutĂ©e Ă  la requĂȘte).

Cependant, si nous revenons Ă  notre systĂšme, nous obtenons un diagramme similaire :

Des modĂšles architecturaux pratiques

Comme vous pouvez le constater, le systĂšme est devenu radicalement plus complexe. DĂ©sormais, il ne s'agit plus simplement d'un mini-processus qui stocke les fichiers localement. DĂ©sormais, ce qui est requis n'est pas le support le plus simple, le contrĂŽle de version de l'API, etc. Par consĂ©quent, une fois tous les diagrammes dessinĂ©s, il est prĂ©fĂ©rable d’évaluer en dĂ©tail si l’extensibilitĂ© en vaut le coĂ»t. Cependant, si vous souhaitez pouvoir Ă©tendre le systĂšme (y compris pour travailler avec un nombre encore plus grand d'utilisateurs), vous devrez alors opter pour des solutions similaires. Mais, par consĂ©quent, le systĂšme est architecturalement prĂȘt pour une charge accrue (presque tous les composants peuvent ĂȘtre clonĂ©s pour une mise Ă  l'Ă©chelle horizontale). Le systĂšme peut ĂȘtre mis Ă  jour sans l'arrĂȘter (simplement certaines opĂ©rations seront lĂ©gĂšrement ralenties).

Comme je l'ai dit au tout dĂ©but, un certain nombre de services Internet ont commencĂ© Ă  recevoir une charge accrue. Et certains d’entre eux ont simplement commencĂ© Ă  ne plus fonctionner correctement. En fait, les systĂšmes ont Ă©chouĂ© prĂ©cisĂ©ment au moment oĂč l’entreprise Ă©tait censĂ©e gagner de l’argent. Autrement dit, au lieu de diffĂ©rer la livraison, au lieu de suggĂ©rer aux clients de « planifier votre livraison pour les mois Ă  venir », le systĂšme leur disait simplement « allez chez vos concurrents ». En fait, c’est le prix d’une faible productivitĂ© : les pertes se produiront prĂ©cisĂ©ment au moment oĂč les profits seraient les plus Ă©levĂ©s.

Conclusion

Toutes ces approches Ă©taient connues auparavant. Le mĂȘme VK utilise depuis longtemps l'idĂ©e de l'hĂ©bergement de contenu statique pour afficher des images. De nombreux jeux en ligne utilisent le systĂšme Sharding pour diviser les joueurs en rĂ©gions ou pour sĂ©parer les emplacements de jeu (si le monde lui-mĂȘme en est un). L’approche Event Sourcing est activement utilisĂ©e dans le courrier Ă©lectronique. La plupart des applications de trading oĂč les donnĂ©es sont reçues en permanence sont en fait construites sur une approche CQRS afin de pouvoir filtrer les donnĂ©es reçues. Eh bien, la mise Ă  l'Ă©chelle horizontale est utilisĂ©e dans de nombreux services depuis assez longtemps.

Mais surtout, tous ces modĂšles sont devenus trĂšs faciles Ă  appliquer dans les applications modernes (s’ils sont appropriĂ©s, bien sĂ»r). Les cloud offrent immĂ©diatement le partage et la mise Ă  l'Ă©chelle horizontale, ce qui est beaucoup plus simple que de commander vous-mĂȘme diffĂ©rents serveurs dĂ©diĂ©s dans diffĂ©rents centres de donnĂ©es. CQRS est devenu beaucoup plus simple, ne serait-ce que grĂące au dĂ©veloppement de bibliothĂšques telles que RX. Il y a environ 10 ans, un site Web rare pouvait prendre en charge cela. Event Sourcing est Ă©galement incroyablement simple Ă  mettre en place grĂące aux conteneurs prĂȘts Ă  l'emploi avec Apache Kafka. Il y a 10 ans, cela aurait Ă©tĂ© une innovation, maintenant c'est monnaie courante. C'est la mĂȘme chose avec l'hĂ©bergement de contenu statique : grĂące Ă  des technologies plus pratiques (y compris le fait qu'il existe une documentation dĂ©taillĂ©e et une grande base de donnĂ©es de rĂ©ponses), cette approche est devenue encore plus simple.

En consĂ©quence, la mise en Ɠuvre d'un certain nombre de modĂšles architecturaux plutĂŽt complexes est dĂ©sormais devenue beaucoup plus simple, ce qui signifie qu'il est prĂ©fĂ©rable de l'examiner de plus prĂšs Ă  l'avance. Si dans une application vieille de dix ans l'une des solutions ci-dessus a Ă©tĂ© abandonnĂ©e en raison du coĂ»t Ă©levĂ© de mise en Ɠuvre et d'exploitation, maintenant, dans une nouvelle application, ou aprĂšs refactoring, vous pouvez crĂ©er un service qui sera dĂ©jĂ  architecturalement Ă  la fois extensible ( en termes de performances) et prĂȘt Ă  rĂ©pondre aux nouvelles demandes des clients (par exemple pour localiser des donnĂ©es personnelles).

Et surtout : n’utilisez pas ces approches si vous avez une application simple. Oui, ils sont beaux et intĂ©ressants, mais pour un site avec une frĂ©quentation maximale de 100 personnes, on peut souvent se contenter d'un monolithe classique (au moins Ă  l'extĂ©rieur, tout Ă  l'intĂ©rieur peut ĂȘtre divisĂ© en modules, etc.).

Source: habr.com

Ajouter un commentaire