Serveur d'impression

Obscurcir la complexité – Bien choisir son serveur d impression

Par Titanfall , le 10 août 2019 - 32 minutes de lecture

Clés à emporter

  • Si cela est bien fait, le développement de logiciels piloté par les modèles peut masquer une partie de la complexité, mais vous devrez traiter la sortie du code source comme des artefacts de construction et s'approprier les modèles. La gestion des modèles générant du code est une sorte de méta-programmation à laquelle la plupart des développeurs ne sont pas habitués.
  • Les applications Twelve Factor peuvent vraiment atteindre une complexité inférieure, mais uniquement lorsqu'elles sont intégrées à des magasins de données matures ou stables (c'est-à-dire ennuyeux).
  • Vous pouvez réduire la complexité de l’orchestration de microservices en intégrant une intelligence intersectorielle limitée dans les connecteurs, mais vous devez être prudent, car trop d’intelligence crée l’effet inverse.
  • Vous pouvez écrire de plus petites applications lorsque vous utilisez un framework Heavyweight, mais sachez que l'efficacité peut en souffrir, que ce type de service est plus difficile à optimiser et que le débogage de certains types de problèmes peut prendre plus de temps.
  • Avec la programmation réactive, vous finissez par échanger la complexité du backend pour la complexité du front.

L’une des tâches les plus importantes des architectes logiciels est de gérer la complexité de leurs systèmes afin d’atténuer les perturbations de publication tout en maintenant une vitesse suffisante des fonctionnalités. Les systèmes peuvent être simplifiés, mais seulement de beaucoup. Les logiciels modernes doivent répondre à de nombreuses exigences sophistiquées pour être compétitifs. Lorsque nous ne pouvons pas réduire la complexité, nous essayons de la cacher ou de la déplacer.

Les architectes logiciels ont tendance à gérer cette complexité avec les stratégies traditionnelles suivantes:

  • Ils peuvent réduire la taille de leurs applications en réutilisant des frameworks génériques ou en utilisant des générateurs de code par programme.
  • Ils facilitent la montée en puissance de leurs systèmes en surveillant de près l'état de l'état de l'application.
  • Ils conçoivent des systèmes qui se dégradent gracieusement sous une charge croissante ou une panne partielle.
  • Enfin, ils normalisent les charges de travail en passant à des systèmes éventuellement cohérents.

Entrons plus en détail sur ces différentes stratégies et sur le contexte historique dans lequel chaque stratégie a été formée, afin de mieux comprendre les avantages et les inconvénients de chaque approche.

Pour chaque stratégie, je mentionne des exemples de technologies complémentaires pour les développeurs Java, JavaScript, Python et .NET. Ces listes ne sont en aucun cas complètes, alors veuillez accepter mes excuses si je ne mentionne pas vos favoris.

C’est seulement un modèle

MDSD, ou développement de logiciels basé sur des modèles, augmente la vitesse des fonctionnalités, car les développeurs gagnent du temps en écrivant moins de code standard. Au lieu de cela, un modèle est spécifié et un générateur combine le modèle avec des modèles du code standard afin de générer le code que les développeurs devaient écrire à la main.

Ma première exposition à MDSD remonte au temps de CASE (ingénierie logicielle assistée par ordinateur). Il a refait surface lorsque UML (langage de modélisation unifié) était à son apogée. À l’époque, le problème avec MDSD était qu’il était prévu de générer tout le code, ce qui signifie que le type de modèle requis pour saisir toutes les exigences possibles était si complexe qu’il était plus facile d’écrire le code.

MDSD fait un retour en force grâce à une technologie appelée Swagger (les nouvelles versions de la spécification de modélisation sont maintenant organisées par OpenAPI Initiative), dans laquelle vous spécifiez un modèle correspondant à l'apparence de vos API. Le modèle et un ensemble de modèles sont entrés dans un générateur, qui génère du code standard qui met en surface les API. Il existe des modèles distincts pour le code qui produisent et utilisent l'API modélisée, et des modèles de démarrage sont disponibles en ligne pour à peu près n'importe quelle pile technologique pertinente.

Par exemple, examinez les modèles Swagger pour Spring Boot, qui génèrent les contrôleurs REST, les POJO de requête et de réponse annotés de Jackson et divers codes passe-partout d'applications. Il appartient aux développeurs d’ajouter la logique et les détails d’implémentation (accès à la base de données, par exemple) nécessaires pour satisfaire les exigences de chaque API. Pour éviter aux ingénieurs de modifier les fichiers générés par Swagger, utilisez l’injection d’héritage ou de dépendance.

Comment MDSD peut-il masquer la complexité de votre code d'application? C'est délicat mais cela peut être fait. Le générateur génère le code qui implémente les ressources de l'API afin que les développeurs n'aient pas à s'inquiéter de le coder. Toutefois, si vous utilisez le générateur en tant qu’assistant de code à utilisation unique et que vous validez la sortie dans votre référentiel de code source contrôlé par la version (par exemple, git), vous n’avez fait qu’économiser du temps de codage initial. Vous n'avez rien caché car les développeurs vont devoir étudier et maintenir le code généré.

Pour vraiment masquer la complexité de ce code, vous devez enregistrer le modèle dans votre référentiel de code source contrôlé par la version, mais pas le code source généré. Vous devez générer cette source de sortie à partir du modèle chaque fois que vous générez le code. Vous devrez ajouter cette étape du générateur à tous vos pipelines de construction. Les utilisateurs de Maven voudront configurer le plugin swagger-codegen-maven-plugin dans leur fichier pom. Ce plugin est un module du projet swagger-codegen.

Que faire si vous devez apporter des modifications au code source généré? C'est pourquoi vous devrez assumer la propriété des modèles et les enregistrer dans votre référentiel de code source contrôlé par la version. Ce sont des modèles de moustache, qui ressemblent au code à générer avec des paramètres de substitution délimités par des accolades bouclées et une logique de branche de décision parsemée de points. La programmation par modèle est une forme de méta-programmation assez avancée.

En fin de compte, le mieux que vous puissiez espérer avec MDSD est que cela puisse masquer la complexité pour vos développeurs débutants, mais au détriment de la prise en charge de ces modèles par vos développeurs principaux.

Développement de logiciels piloté par les modèles
Avantages Les inconvénients
  • Taille d'application plus petite
  • Vélocité accrue
  • Pipelines bulidiques plus compliqués
  • Les modèles doivent être maintenus

Sur l'origine des systèmes informatiques

En 2011, les employés de Heroku ont publié un recueil de meilleures pratiques pour l’écriture de logiciels modernes, basés sur le cloud et orientés services. Ils l'appelaient l'appli à douze facteurs. Pour mieux comprendre pourquoi ces douze facteurs réduisent réellement la complexité, nous passons brièvement en revue l’évolution des systèmes informatiques, qui sont passés d’une configuration simple à une machine à des grappes complexes de machines virtuelles connectées dans un réseau défini par logiciel.

Pendant longtemps, les applications ont été conçues pour fonctionner sur un seul ordinateur. Si vous voulez que l'application traite plus de demandes, vous devez la faire évoluer en l'installant sur un ordinateur plus grand. Les systèmes ont évolué pour devenir des applications à deux niveaux dans lesquelles des centaines d'utilisateurs exécutaient un programme client spécialisé sur leurs ordinateurs connectés à un programme de base de données s'exécutant sur un seul serveur.

La prochaine étape de l'évolution de l'informatique a été celle des systèmes à trois niveaux dans lesquels les programmes clients se connectaient à un serveur d'applications qui accéderait au serveur de base de données. Les applications Web ont remplacé les applications client-serveur, car il était plus facile de déployer la partie client (en supposant qu'un navigateur Web moderne était installé sur l'ordinateur de tout le monde) et que vous pouviez prendre en charge davantage d'utilisateurs connectés au système. La mise à l'échelle (remplacer un ordinateur par un ordinateur plus grand) est devenue moins attrayante que la mise à l'échelle (passer d'un ordinateur à plusieurs ordinateurs). Afin de gérer la charge utilisateur supplémentaire, ce serveur d'applications unique a été étendu à un cluster d'ordinateurs s'exécutant sous un équilibreur de charge. Les serveurs de base de données pourraient être mis à l’échelle par des techniques connues comme le sharding (pour les écritures) et la réplication (pour les lectures). À l'époque, tous ces serveurs étaient déployés soit dans les locaux de l'entreprise qui les utilisait, soit dans un centre de données loué.

Pendant environ trente ans, l'option la plus viable pour les logiciels de base de données était les bases de données relationnelles, également appelées bases de données SQL, car le code de l'application communiquait avec elles via des commandes écrites dans le langage de requête structurée. Il existe de nombreuses bases de données relationnelles parmi lesquelles choisir. MySQL, MariaDB et PostgreSQL sont des bases de données open source populaires. Oracle et MS SQL Server sont des bases de données propriétaires populaires.

Une prolifération d'autres options est devenue disponible au cours des dix dernières années environ. Il existe maintenant une catégorie appelée bases de données NoSQL qui comprend des bases de données à colonnes étendues telles que Cassandra, des bases de données de valeurs clés comme Aerospike, des bases de données de documents similaires à MongoDB, des bases de données de graphes telles que Neo4j et des index inversés de style Elasticsearch. Encore plus récemment, les bases de données multimodèles et distribuées ont gagné en popularité. Avec des bases de données multimodèles, vous pouvez appeler les API SQL et NoSQL sur une seule installation de base de données. Les bases de données distribuées gèrent le sharding et la réplication sans complexité supplémentaire dans le code de l'application. YugaByte et Cosmos DB sont à la fois multimodaux et distribués.

Avec l'avènement du cloud, les entreprises ne sont plus obligées de faire appel à des ingénieurs qui savent assembler et câbler des bâtis d'ordinateurs ou signer des contrats de location d'une durée de cinq ans avec des fabricants d'ordinateurs et des fournisseurs de services d'hébergement gérés. Afin de réellement réaliser ces économies d’échelle, les ordinateurs ont été virtualisés et sont devenus plus éphémères. Les logiciels ont dû être repensés afin de prendre en charge plus facilement tous ces changements dans la manière dont ils ont été déployés.


Déploiement typique où deux microservices et une base de données sont mis à l'échelle.

Les applications qui suivent correctement ces douze facteurs gèrent facilement cette prolifération de matériel avec une complexité minimale. Concentrons-nous sur les facteurs 6 (processus), 8 (concurrence) et 9 (disponibilité).

Vous serez en mesure de faire évoluer votre application plus facilement si l'application est conçue pour s'exécuter sur de nombreux processus sans état. Sinon, tout ce que vous pouvez faire facilement est de passer à l’échelle supérieure. C'est ce que le facteur 6 est tout au sujet. Il est correct de mettre en cache des données dans un cluster externe afin d’accélérer la latence moyenne ou de protéger la ou les bases de données sous-jacentes contre l’écrasement, mais le cache ne doit jamais contenir de données qui ne se trouvent pas déjà dans la ou les bases de données. Vous pouvez perdre le cache à tout moment sans perdre les données réelles.

Le facteur 8 concerne la concurrence. Ces processus doivent être regroupés en clusters de manière à ce que chaque cluster de processus traite le même type de demandes. Votre logiciel sera beaucoup plus simple si ces processus ne partagent aucun état autre que l’utilisation de bases de données. Si ces processus partagent un état interne, ils doivent alors se connaître, ce qui rendra plus difficile et plus compliqué la montée en puissance progressive en ajoutant davantage de processus au cluster.

Votre application sera plus sensible aux changements de charge et robuste à la déstabilisation si chaque processus peut s'initialiser rapidement et se terminer normalement. C'est le facteur 9, la disponibilité. La mise à l'échelle dynamique vous permet d'ajouter rapidement et automatiquement plus de processus pour gérer une charge accrue, mais cela ne fonctionne que si chaque processus n'a pas besoin de beaucoup de temps pour démarrer avant d'être prêt à accepter les demandes. Parfois, un système se déstabilise et le moyen le plus rapide de remédier à la panne consiste à redémarrer tous les processus, mais cela ne fonctionne que si chaque processus peut se terminer rapidement sans perdre de données.

Vous éviterez beaucoup de bugs et de fragilité si vous concevez vos systèmes de telle sorte que le flux de demandes entrantes soit géré par de nombreux processus exécutés simultanément. Ces processus peuvent, mais ne doivent pas nécessairement être multithreads. Ces processus devraient pouvoir démarrer rapidement et s'arrêter en douceur. Plus important encore, ces processus devraient être sans état et ne rien partager.

De toute évidence, il n'y a pas beaucoup de demande pour des applications qui ne peuvent se souvenir de rien, alors où va l'État? La réponse est dans la base de données, mais les applications de base de données sont aussi des logiciels. Pourquoi est-il acceptable que les bases de données soient à état, alors que les applications ne le sont pas? Nous avons déjà expliqué que les applications doivent être en mesure de fournir des fonctionnalités relativement rapides. Il n'en va pas de même pour les logiciels de base de données. Il faut beaucoup de temps d’ingénierie, de réflexion et d’efforts pour réussir la gestion des états avec une charge élevée. Une fois que vous y êtes arrivé, vous ne voulez pas faire beaucoup de gros changements, car les logiciels avec état sont très complexes et faciles à décomposer.

Comme mentionné précédemment, les technologies de bases de données prolifèrent, dont beaucoup sont nouvelles et relativement peu testées. Vous pouvez obtenir une certaine utilité d'une application apatride après seulement quelques mois d'ingénierie. Certains ou la plupart de ces ingénieurs peuvent être des diplômés récents sans grande expérience professionnelle. Une application avec état est complètement différente. Je ne parierais pas sur une technologie de base de données ne nécessitant pas au moins deux décennies d'efforts d'ingénierie. (C’est des décennies d’ingénierie, et non de calendrier.) Ces ingénieurs doivent être des professionnels chevronnés, très intelligents, dotés d’une vaste expérience de l’informatique distribuée et de nombreuses compétences informatiques. Si vous utilisez un moteur de base de données non testé ou immature, vous finirez par introduire une complexité supplémentaire dans votre application afin de contourner les bugs et les limitations de la base de données immature. Une fois que les bogues dans la base de données ont été corrigés, vous devrez ré-architecturer votre application pour supprimer la complexité désormais inutile.

Innover votre pile technologique
Avantages Les inconvénients
  • L'adoption d'une nouvelle technologie sans état peut être amusante et confère un avantage concurrentiel avec peu de risque.
  • L'adoption de nouvelles technologies dynamiques est très risquée.
  • Il est probable que la complexité de vos applications augmentera au lieu de la diminuer.

C'est une série de tubes après tout

À mesure que les systèmes évoluaient d'une application à une grappe d'applications et de bases de données interconnectées, un corpus de connaissances était répertorié afin de fournir des conseils sur les moyens les plus efficaces pour que ces applications puissent interagir les unes avec les autres. Au début des années 2000, un livre sur les modèles d’intégration des entreprises (EIP) a été publié. Il décrit de manière plus formelle ce corpus de connaissances.

À l'époque, un style d'interaction de service connu sous le nom d'architecture orientée service est devenu populaire. En SOA, les applications communiquaient entre elles via un bus de service d'entreprise (ESB) également programmé pour manipuler les messages et les acheminer en fonction de règles de configuration étroitement conformes à EIP.

Les moteurs de flux de travail sont une technologie similaire, basée sur les réseaux de Petri, qui était davantage axée sur les entreprises. Il a été vendu sur le principe que les non-ingénieurs pourraient écrire les règles, mais n'a jamais vraiment tenu sa promesse.

Ces approches ont introduit beaucoup de complexité inutile et non appréciée qui les a fait tomber en disgrâce. La configuration est devenue un enchevêtrement complexe de règles interconnectées qui sont devenues très résistantes aux changements au fil du temps. Pourquoi est-ce? C’est le même problème que de demander à MDSD de modéliser toutes les exigences. Les langages de programmation peuvent nécessiter plus de connaissances en ingénierie que les langages de modélisation, mais ils sont aussi plus expressifs. Il est beaucoup plus facile d’écrire ou de comprendre un petit fragment de code précédemment écrit pour traiter une exigence EIP, que de créer une spécification de modèle BPMN large et compliquée. Camel (projet Apache) et Mulesoft (acquis par Salesforce en 2018) sont des ESB qui tentent de simplifier leurs technologies respectives. J'espère qu'ils réussissent.

La réaction à la SOA à saveur de flux de travail / ESB est devenue MSA, ou architecture de microservice. En 2014, James Lewis et Martin Fowler ont résumé les différences entre MSA et SOA. Avec SOA, vous disposiez de points de terminaison stupides et de canaux intelligents. Avec MSA, vous disposiez de points de terminaison intelligents et de tubes muets. La complexité a été réduite, mais peut-être trop. Ces systèmes étaient fragiles et non résilients (c'est-à-dire facilement déstabilisés) pendant les périodes de défaillance partielle ou de dégradation des performances. Il y avait aussi beaucoup de duplicité dans les microservices distincts qui devaient chacun mettre en œuvre les mêmes préoccupations transversales, telles que la sécurité. Cela est vrai (quoique dans une moindre mesure) même si chaque implémentation incorpore simplement la même bibliothèque partagée.

Ce qui a suivi a été l’introduction des passerelles API et des maillages de service, qui sont tous deux des versions améliorées des équilibreurs de charge de couche 7. Le terme «couche 7» fait référence au modèle d'interconnexion OSI ou Open Systems introduit dans les années 80.

Lorsque les appels provenant d'Internet ou de l'intranet sont destinés à des microservices sur le backend, ils passent par une passerelle API qui gère des fonctionnalités telles que l'authentification, la limitation de débit et la consignation des demandes, supprimant ainsi ces exigences de chaque microservice.

Les appels d'un microservice vers un autre microservice passent par un maillage de service qui traite des problèmes tels que la cloison et la coupure de circuit. Lorsque les demandes adressées à un service expirent trop souvent, le maillage de service échoue immédiatement (pendant un certain temps) des appels futurs au lieu de tenter de passer les appels réels. Cela évite que le service qui ne répond pas ne provoque pas la non-réponse des services dépendants en raison de tous leurs threads en attente du service original qui ne répond pas. Ce comportement est similaire à celui d’une cloison sur un navire empêchant une inondation de se propager au-delà d’un compartiment. En cas de coupure de circuit, le maillage de service échoue immédiatement les appels (pendant un certain temps) vers un service qui a récemment échoué pour la plupart de ses appels précédents. La raison d'être de cette stratégie est que le service défaillant a été submergé. Empêcher les appels vers ce service lui donnera une chance de récupérer.

Déploiement d’une passerelle d’API et d’un maillage de service.

Les passerelles d'API et les maillages de service rendent les microservices plus résilients sans introduire de complexité supplémentaire dans le code de microservice lui-même. Cependant, ils augmentent les coûts opérationnels en raison de la responsabilité supplémentaire de maintenir l'intégrité de la passerelle API et / ou du maillage de service.

MSA vs SOA
Avantages Les inconvénients
  • Pour EIP, le code est plus simple que la configuration.
  • Les passerelles API réduisent la duplicité de la mise en œuvre de problèmes transversaux.
  • Les mailles de service augmentent la résilience.
  • Les ESB compliquent la compréhension des systèmes et la prévision du comportement.
  • Les systèmes utilisant des moteurs de flux de travail ont plus de chances de résister aux changements au fil du temps.
  • Les passerelles API et les maillages de service introduisent des coûts opérationnels supplémentaires.

Marche des cadres

Un autre moyen de réduire la quantité de code que les développeurs doivent écrire consiste à utiliser un framework d’application. Un framework est juste une bibliothèque de routines à usage général qui implémentent des fonctionnalités communes à toutes les applications. Certaines parties de la structure se chargent en premier et finissent par appeler votre code plus tard.

Comme je l'ai mentionné plus tôt, les bases de données relationnelles ont été développées au milieu des années 70 et étaient si utiles qu'elles sont restées populaires tout au long des tendances technologiques décrites précédemment. Ils sont toujours populaires aujourd'hui, mais leur utilisation dans le monde des applications Web introduit beaucoup de complexité. Les connexions aux bases de données relationnelles ont une longue durée de vie, mais les requêtes Web classiques sont sans état et de courte durée. Le résultat final est que les services multithreads doivent faire face à cette complexité en utilisant une technique appelée regroupement de connexions. Les applications mono-threadées sont moins efficaces de cette manière; ils doivent donc dépendre davantage du sharding et de la réplication.

La programmation orientée objet est devenue très populaire à l'ère client-serveur et a maintenu sa popularité depuis. Les données relationnelles ne s'intègrent pas très facilement dans la structure orientée objet. Des cadres de cartographie relationnelle objet ont donc été développés pour tenter de masquer ce type de complexité. Les structures ORM populaires incluent Hibernate, SQLAlchemy, LoopBack et Entity Framework.

Aux débuts du développement d'applications Web, tout était construit dans ce qui allait devenir le monolithe. L’interface utilisateur graphique ou l’interface graphique (essentiellement HTML, CSS et JavaScript) a été générée côté serveur. Des modèles tels que MVC (contrôleur de vue de modèle) ont été utilisés pour coordonner le rendu de l'interface graphique avec l'accès aux données, les règles métier, etc. Il existe en réalité de nombreuses variantes de MVC, mais, aux fins de cet article, je les mets tous dans la même catégorie. comme MVC. MVC est toujours présent et les infrastructures MVC modernes populaires incluent Play, Meteor, Django et ASP.NET.

Au fil du temps, ces types d’applications sont devenus volumineux et encombrants; si grande que leur comportement était difficile à comprendre ou à prédire. Apporter des modifications à l'application était risqué et la publication de nouvelles versions était perturbante car il était difficile de tester et de vérifier l'exactitude de ces systèmes trop complexes. Il a fallu beaucoup de temps d'ingénierie pour résoudre rapidement le code buggy qui a été déployé sans vérification préalable. Lorsque vous êtes obligé de réparer quelque chose rapidement, vous n'avez pas le temps de trouver la meilleure solution, ce qui risque de faire rentrer un code de mauvaise qualité. L'intention est de remplacer ce code de mauvaise qualité par un code de bonne qualité.

La solution consistait à scinder le monolithe en plusieurs composants ou microservices pouvant être publiés séparément. Le code de l'interface graphique a été transféré dans ce qui s'appelle maintenant SPA (applications à page unique) ainsi que dans les applications mobiles natives. L'accès aux données et les règles de gestion ont été conservés côté serveur et divisés en plusieurs services. Les frameworks de microservice populaires incluent Flask et Express. Spring Boot et Dropwizard sont les conteneurs de servlets basés sur Jersey les plus populaires pour les développeurs Java.

Les frameworks de microservices étaient à l’origine simples, faciles à apprendre et présentaient un comportement facile à comprendre et à prévoir. Les applications construites sur ces infrastructures légères ont pris de l'ampleur avec le temps en raison des facteurs de complexité mentionnés ci-dessus. Plus une application devient volumineuse, plus elle ressemble à un monolithe. Lorsqu'ils ne divisaient pas les gros microservices en de plus petits, les architectes ont commencé à chercher des moyens de réduire la taille des applications en masquant la complexité associée dans le cadre. L'utilisation de logiciels basés sur l'opinion, de modèles de conception basés sur des annotations et le remplacement du code par la configuration ont réduit le nombre de lignes de code dans les applications, mais ont rendu les frameworks plus lourds.

Les applications qui utilisent un framework Heavyweight ont généralement moins de lignes de code et une vitesse de traitement plus rapide, mais cette forme de complexité masquée présente des inconvénients. De par leur nature même, les frameworks sont plus polyvalents que les applications, ce qui signifie qu'il faut beaucoup plus de code pour faire le même travail. Bien que vous ayez moins de code d’application personnalisé, l’exécutable réel, qui inclut le code d’infrastructure approprié, est beaucoup plus volumineux. Cela signifie qu'il faudra plus de temps à l'application pour démarrer car tout ce code supplémentaire est chargé en mémoire. Tout ce code invisible supplémentaire signifie également que les traces de la pile (qui sont écrites dans le journal des applications chaque fois qu'une exception inattendue est levée) seront beaucoup plus longues. Une trace de pile plus grande prend plus de temps à un ingénieur pour lire et comprendre lors du débogage.

Dans le meilleur des cas, le réglage des performances peut être un peu un art noir. Il faut parfois beaucoup d'essais et d'erreurs pour parvenir à la bonne combinaison de tailles de pool de connexions, de durées d'expiration de cache et de valeurs de délai de connexion. Cela devient encore plus décourageant lorsque vous ne voyez pas le code que vous essayez d’accorder. Ces frameworks sont open source, vous pouvez donc étudier le code mais la plupart des développeurs ne le font pas.

Cadres légers vs lourds
Avantages Les inconvénients
  • Les frameworks légers sont plus faciles à déboguer et à ajuster.
  • Les frameworks lourds augmentent la vélocité des fonctionnalités et réduisent les perturbations de publication.
  • Les applications ont moins de code en double.
  • Les frameworks légers nécessitent que les développeurs écrivent plus de code.
  • Les frameworks lourds prennent plus de temps à démarrer et à s'arrêter.
  • Signifie généralement accepter le code de la boîte noire dans un cadre

Cohérence éventuelle

Au lieu de traiter immédiatement chaque demande d'API de manière synchrone, les systèmes réactifs transmettent des messages de manière asynchrone à ses sous-systèmes internes afin de traiter éventuellement chaque demande d'API.

Il est difficile de dire quand la programmation réactive a été introduite. Le Manifeste réactif a été publié en juillet 2013, mais il en existait beaucoup ou des précurseurs. Le modèle pubsub, ou publication-souscription, a été introduit au milieu des années 80. Le traitement des événements complexes, ou CEP, a brièvement connu une certaine popularité dans les années 90. Le premier article que j'ai vu sur SEDA, ou architecture événementielle mise en scène, a été publié vers la fin de 2001. Le sourcing d'événement est une variation récente sur le thème de la programmation réactive. Les systèmes réactifs peuvent être codés dans le style pubsub ou sous forme de flux de messages dans des langages de script de domaine qui ressemblent à la programmation fonctionnelle.

Lorsqu'un système de programmation réactif est distribué sur plusieurs ordinateurs, un courtier de messages est généralement impliqué (mais pas toujours). Parmi les courtiers les plus populaires, citons Kafka, RabbitMQ et ActiveMQ. Les gens de Kafka ont récemment publié une bibliothèque côté client appelée Kafka Streams.

Déploiement typique pour un système distribué, totalement réactif.

ReactiveX est un framework réactif très populaire avec des bibliothèques pour de nombreux langages de programmation. Pour les programmeurs Java, il existe Spring Integration ou Spring Cloud Data Flow, Vert.x et Akka.

Voici comment les architectes utilisent la programmation réactive pour masquer la complexité. Les appels aux microservices deviennent asynchrones, ce qui signifie que tout ce qui a été demandé à l'API n'a pas à être fait lors du retour des appels. Ceci est également connu sous le nom de cohérence éventuelle. Cela rend ces microservices plus résilients face aux pannes partielles ou aux performances dégradées des bases de données sans introduire beaucoup de complexité supplémentaire. Vous n'avez pas à vous inquiéter du fait que l'appelant arrive à expiration et à soumettre de nouveau lorsque la transaction d'origine est toujours en cours d'exécution. Si une ressource n'est pas disponible, attendez qu'il soit à nouveau disponible. Je dois admettre que les développeurs débutants peuvent avoir du mal à déboguer les programmes réactifs (surtout s’ils sont codés dans le style pubsub), mais c’est principalement parce qu’ils ne connaissent pas ce paradigme.

Alors, où est passée la complexité? Il existe beaucoup de complexité dans les courtiers de messages modernes, mais vous pourrez probablement utiliser l'un de ceux-ci sans avoir à écrire le vôtre. Comme toute technologie, ils ont leurs propres mises en garde, mais ils ont des limites très raisonnables.

Pour le développement d'applications, la complexité a été déplacée vers le client. La cohérence éventuelle pourrait être merveilleuse pour les systèmes backend, mais elle est terrible pour les humains. Vous ne vous inquiétez peut-être pas lorsque vos photos de vacances atteignent tous vos amis de votre réseau social, mais si vous êtes une entreprise qui négocie une commande en plusieurs étapes interconnectée, vous voudrez alors savoir précisément quand chaque partie de votre commande est soumise, validée. approuvé, programmé et éventuellement rempli.

Pour que l'interface graphique puisse répondre à ce besoin psychologique très humain, elle devra informer l'utilisateur lorsque ce qui a été demandé à propos du backend est terminé. Comme l'appel API n'est pas synchrone, le client devra trouver un autre moyen. L'interrogation de l'API pour les mises à jour de statut ne se fait pas correctement. Cela signifie que le navigateur Web ou le périphérique mobile devra utiliser une connexion dynamique et à longue durée de vie lui permettant de recevoir les mises à jour du serveur sans aucune invite. Auparavant, vous pouviez étendre les serveurs XMPP à cette fin. Pour les navigateurs Web modernes, la prise en charge des Websockets et des événements envoyés par le serveur est satisfaisante. Spring WebFlux, socket.io et SignalR sont trois bibliothèques populaires permettant aux services côté serveur de communiquer avec le javascript côté client de cette manière.

Les navigateurs Web imposent des limites sur ces connexions, de sorte que l'application cliente devra partager la même connexion pour recevoir toutes les notifications. Comme la plupart des équilibreurs de charge ferment les connexions inactives, l'application doit en tenir compte en envoyant occasionnellement des messages Keep Alive. Les appareils mobiles sont connus pour les échecs de connexion intermittents, nécessitant une logique de reconnexion dans le logiciel client. En outre, il doit exister un mécanisme permettant à l'application cliente d'associer chaque notification (il peut y en avoir plusieurs) à l'appel d'API d'origine. Il doit encore exister un mécanisme permettant de déterminer l’état des appels d’API antérieurs lorsque l’utilisateur revient dans l’application après s’être absenté.

Systèmes réactifs et cohérence éventuelle
Avantages Les inconvénients
  • Les systèmes réactifs sont plus réactifs et résilients.
  • Les systèmes réactifs peuvent réduire la complexité du back-end, en particulier pour les applications utilisant beaucoup de données.
  • Les systèmes réactifs peuvent accroître la complexité du front-end pour que l'application soit émotionnellement satisfaisante pour ses utilisateurs.
  • Les systèmes réactifs distribués hautement évolutifs augmentent les coûts opérationnels avec l'adoption d'un courtier de messages.

Conclusion

Depuis les débuts des premiers ordinateurs centraux jusqu'à aujourd'hui avec le cloud, la complexité des systèmes s'est accrue et les architectes de logiciels ont trouvé de nouveaux moyens de gérer cette complexité. Réduire la complexité sans sacrifier la capacité est, lorsque cela est possible, le meilleur plan d’action. Les applications à douze facteurs offrent d'excellents conseils sur la manière de procéder. Avec EIP, des systèmes réactifs et une cohérence éventuelle, vous pourriez penser que vous réduisez la complexité lorsque vous vous contentez de le déplacer vers une autre partie du système. Parfois, il suffit de cacher la complexité et de nombreux générateurs, infrastructures et connecteurs basés sur des modèles vous aident à le faire, mais cette approche présente à la fois des avantages et des inconvénients. Comme nous l’avons appris avec les applications à douze facteurs et les systèmes réactifs, rien n’accroît la complexité de l’état, alors soyez très prudent et conservateur lorsque vous ajoutez ou augmentez l’état dans vos applications.

Quelle que soit leur réduction, leur masquage ou leur redistribution, les architectes logiciels continueront de gérer la complexité afin de continuer à fournir des logiciels de qualité plus rapidement dans un monde caractérisé par une demande croissante de fonctionnalités, de capacités, de capacités et d'efficacité.

A propos de l'auteur

Glenn Engstrand est un architecte logiciel chez Adobe, Inc. Il travaille principalement avec des ingénieurs afin de fournir des architectures applicatives évolutives, côté serveur, compatibles Twelve Factor. Engstrand était l'un des principaux intervenants lors de la conférence interne des développeurs d'Adobe Advertising Cloud en 2018 et 2017 et lors de la conférence Lucene Revolution en 2012 à Boston. Il est spécialisé dans la fragmentation des applications monolithiques en microservices et dans l’intégration profonde à l’infrastructure de communications en temps réel.

Click to rate this post!
[Total: 0 Average: 0]

Commentaires

Laisser un commentaire

Votre commentaire sera révisé par les administrateurs si besoin.