Créez une architecture évolutive et résiliente avec des microservices dynamiques

Rebonjour. Comme vous le savez, en mars, OTUS lance un tout nouveau cours sur l'architecture et les modèles de conception . Avant le début du cours, beaucoup de matériel a été traduit pour vous sur la création d'une architecture évolutive et résiliente à l'aide de microservices dynamiques. Bonne lecture!




annotation


L'une des tendances les plus importantes de l'architecture industrielle est l'utilisation de microservices au détriment des architectures monolithiques qui perdent en popularité. Grâce à l'architecture cloud, le déploiement de systèmes de microservices est plus productif, flexible et rentable. Quoi qu'il en soit, de nombreuses entreprises ont déjà commencé à passer d'un type d'architecture à un autre, mais ce n'est encore qu'à ses balbutiements. Dans cet article, nous résolvons les problèmes résultant de la nécessité de développer un système évolutif et tolérant aux pannes basé sur des microservices. Dans nos expériences, nous considérons deux types de microservices, simples et avancés, et montrons que la solution proposée est innovante par son comportement dynamique.

1. Introduction


Au cours des dernières décennies, l'histoire des langages de programmation et des paradigmes informatiques a été caractérisée par une attention accrue à la distribution et à la modularisation pour améliorer la réutilisation et la fiabilité du code.

Il était nécessaire d'augmenter la quantité et la qualité des logiciels [1]. L'un des facteurs clés pour clarifier les divers désaccords associés à la conception innovante est l'adéquation de l'utilisation de divers outils pour la conception et le développement de systèmes logiciels plus avancés [2]. Un grand succès dans ce processus a récemment été démontré par des systèmes basés sur des microservices [3], qui sont un paradigme architectural axé sur diverses applications (par exemple, pour les personnes handicapées) [3]. Sous les auspices des microservices, l'intérêt pour l'architecture et le design ne cesse de croître. Attributs de qualité (par exemple, évolutivité, performances et tolérance aux erreurs) ou sélection de modèle, tels que «contrat de service» [5] ou API Gateway,ne violent plus le principe YAGNI («Vous n'en aurez pas besoin» - «souffrant d'erreurs BDUF» («Big Design Up Front» - «Design à grande échelle d'abord»)). La principale question de recherche à laquelle cet article entend répondre est de savoir comment développer un système basé sur des microservices avec la même simplicité qu'un système monolithique? De plus, à partir du sujet précédent, comment pouvons-nous créer un environnement qui fournit une distribution dynamique de la puissance de calcul entre les clients? Notre hypothèse de recherche suggère d'utiliser une architecture système client-serveur qui combine l'informatique distribuée et les microservices pour résoudre ces problèmes.La principale question de recherche à laquelle cet article entend répondre est de savoir comment développer un système basé sur des microservices avec la même simplicité qu'un système monolithique? De plus, à partir du sujet précédent, comment pouvons-nous créer un environnement qui fournit une distribution dynamique de la puissance de calcul entre les clients? Notre hypothèse de recherche suggère d'utiliser une architecture système client-serveur qui combine l'informatique distribuée et les microservices pour résoudre ces problèmes.La principale question de recherche à laquelle cet article entend répondre est de savoir comment développer un système basé sur des microservices avec la même simplicité qu'un système monolithique? De plus, à partir du sujet précédent, comment pouvons-nous créer un environnement qui fournit une distribution dynamique de la puissance de calcul entre les clients? Notre hypothèse de recherche suggère d'utiliser une architecture système client-serveur qui combine l'informatique distribuée et les microservices pour résoudre ces problèmes.qui assure une répartition dynamique de la puissance de calcul entre les clients? Notre hypothèse de recherche suggère d'utiliser une architecture système client-serveur qui combine l'informatique distribuée et les microservices pour résoudre ces problèmes.qui assure une répartition dynamique de la puissance de calcul entre les clients? Notre hypothèse de recherche suggère d'utiliser une architecture système client-serveur qui combine l'informatique distribuée et les microservices pour résoudre ces problèmes.

La structure du document est la suivante: la section 2 fournit un bref aperçu de la littérature actuelle expliquant l'importance des microservices, y compris deux services bien connus offerts par Azure, et la section 3 traite de l'architecture proposée. La section 4 discute de l'évaluation de ce système avant de tirer des conclusions dans la dernière section.

2. Analyse documentaire des microservices


Grâce à l'architecture cloud, le déploiement de systèmes de microservices est plus productif, flexible et rentable [6]. Cependant, Zimmermann note que les microservices sont un sujet sensible qui est étudié principalement dans le milieu universitaire [7] et l'industrie. Le terme «microservices» a été abordé pour la première fois lors d'un séminaire d'architectes logiciels en Italie en mai 2011 pour décrire ce que les participants considéraient comme un style architectural commun récemment exploré par beaucoup d'entre eux. Un an plus tard, le même groupe a confirmé que le terme «microservices» était le nom le plus approprié. En fait, les microservices ont été développés pour répondre aux problèmes des applications monolithiques ou des architectures orientées services qui compliquent l'évolutivité, la complexité et les dépendances de l'application en cours de développement,ainsi que l'utilisation de mécanismes de communication légers [8-9]. Le monolith étant une application logicielle dont les modules ne peuvent pas être exécutés indépendamment, nous devrions envisager une solution basée sur des microservices, car elle est la seule capable d'exécuter des instructions indépendamment les unes des autres [10-11]. Les grands monolithes deviennent difficiles à entretenir au fil du temps et sont difficiles à évaluer en raison de leur complexité, mais le principal inconvénient est qu'ils limitent l'évolutivité du produit. Un autre problème est qu'ils ne fournissent pas de tolérance aux pannes et ne permettent pas à un composant individuel du système de fonctionner lorsqu'un autre composant ne fonctionne pas, ce qui est possible dans les architectures orientées microservices.Le monolith étant une application logicielle dont les modules ne peuvent pas être exécutés indépendamment, nous devrions envisager une solution basée sur des microservices, car elle est la seule capable d'exécuter des instructions indépendamment les unes des autres [10-11]. Les grands monolithes deviennent difficiles à entretenir au fil du temps et sont difficiles à évaluer en raison de leur complexité, mais le principal inconvénient est qu'ils limitent l'évolutivité du produit. Un autre problème est qu'ils ne fournissent pas de tolérance aux pannes et ne permettent pas à un composant individuel du système de fonctionner lorsqu'un autre composant ne fonctionne pas, ce qui est possible dans les architectures orientées microservices.Le monolith étant une application logicielle dont les modules ne peuvent pas être exécutés indépendamment, nous devrions envisager une solution basée sur des microservices, car elle est la seule capable d'exécuter des instructions indépendamment les unes des autres [10-11]. Les grands monolithes deviennent difficiles à entretenir au fil du temps et sont difficiles à évaluer en raison de leur complexité, mais le principal inconvénient est qu'ils limitent l'évolutivité du produit. Un autre problème est qu'ils ne fournissent pas de tolérance aux pannes et ne permettent pas à un composant individuel du système de fonctionner lorsqu'un autre composant ne fonctionne pas, ce qui est possible dans les architectures orientées microservices.capable d'exécuter des instructions indépendamment les unes des autres [10-11]. Les grands monolithes deviennent difficiles à entretenir au fil du temps et sont difficiles à évaluer en raison de leur complexité, mais le principal inconvénient est qu'ils limitent l'évolutivité du produit. Un autre problème est qu'ils ne fournissent pas de tolérance aux pannes et ne permettent pas à un composant individuel du système de fonctionner lorsqu'un autre composant ne fonctionne pas, ce qui est possible dans les architectures orientées microservices.capable d'exécuter des instructions indépendamment les unes des autres [10-11]. Les grands monolithes deviennent difficiles à entretenir au fil du temps et sont difficiles à évaluer en raison de leur complexité, mais le principal inconvénient est qu'ils limitent l'évolutivité du produit. Un autre problème est qu'ils ne fournissent pas de tolérance aux pannes et ne permettent pas à un composant individuel du système de fonctionner lorsqu'un autre composant ne fonctionne pas, ce qui est possible dans les architectures orientées microservices.et ils ne permettent pas à un composant individuel du système de fonctionner lorsqu'un autre composant ne fonctionne pas, ce qui est possible dans les architectures orientées microservices.et ils ne permettent pas à un composant individuel du système de fonctionner lorsqu'un autre composant ne fonctionne pas, ce qui est possible dans les architectures orientées microservices.

Dans SOA (Service Oriented Architecture), les principaux services sont coordonnés à l'aide de deux méthodes: l'orchestration (où il existe un microservice central qui enverra des demandes à d'autres services et contrôlera l'ensemble du processus en envoyant et recevant des réponses) et la chorégraphie (qui n'implique aucune centralisation, mais chaque service sait à l'avance ce qu'il doit faire) [1]. Comme dans le cas des architectures monolithiques et des architectures SOA, le problème le plus difficile reste la partition du système en services [12]. En outre, vous ne devez en aucun cas négliger la question de la fourniture d'informations confidentielles par le biais d'une distribution incontrôlée de services [13].

Notre architecture combine l'informatique distribuée avec des microservices pour créer un environnement qui permet une distribution dynamique de l'informatique entre les clients. Par informatique distribuée, nous entendons la disponibilité du traitement et du stockage de grandes quantités de données dans le cloud, qui est un élément clé de l'industrie moderne à l'intérieur et à l'extérieur du domaine informatique. Les systèmes de stockage distribués sont conçus pour répondre aux exigences des applications distribuées et avancées en matière de calcul avec une large applicabilité, évolutivité et hautes performances. Une solution bien connue est MapReduce [14], qui orchestre les calculs en triant les serveurs distribués, tout en gérant simultanément diverses tâches, toutes les communications et le transfert de données entre les parties du système,redondance et tolérance aux pannes.

Azure Batch est un autre modèle de programmation utilisé pour exécuter efficacement des applications informatisées en mode parallèle ou à grande échelle, sans configuration manuelle ni gestion d'infrastructure, avec des clusters plus puissants de calcul haute performance (HPC - calcul haute performance) [15]. Pour illustrer ces idées, rappelons-nous le SaaS (logiciel en tant que service) ou les applications clientes qui nécessitent une large exécution [16]. En fait, diverses sociétés informatiques manifestent un intérêt accru pour le SaaS, souhaitant réduire leurs dépenses d'exploitation et, par conséquent, accroître la flexibilité de leur entreprise [17]. Azure Functions est un autre service proposé par les principaux fournisseurs de services cloud,qui permet un lancement à la demande sans avoir besoin de fournir ou de gérer explicitement l'infrastructure [18].

Cela augmente également l'intérêt des applications à lancer facilement de petits morceaux de code ou de «fonctions» dans le cloud. L'intérêt croissant pour l'Internet des objets (IoT) fait d'Azure Functions [19] une excellente solution pour le traitement des données, l'intégration de systèmes et la création d'API et de microservices simples.

3. MĂ©thodologie


Le système proposé structurellement peut être divisé en 3 domaines différents: (1) le client - qui effectuera les tâches assignées par le serveur; (2) serveur - une interface avec un client, le cerveau des applications monolithiques; (3) une zone de gestion de communication client-serveur qui encapsule tous les détails associés au transfert d'exécution du serveur vers le client. Toutes les informations transmises sur le réseau entre le client et le serveur sont cryptées à l'aide de l'algorithme DES (Data Encryption Standard), et la clé est modifiée à l'aide du protocole Diffie-Hellman [20], qui, bien qu'il soit vulnérable dans certaines conditions, encore mis en œuvre dans une variété de solutions de sécurité Internet.

3.1. Architecture du système

Notre système est fortement basé sur l'architecture des systèmes de microservices dynamiques. L'architecture prend comme base le client-serveur, dans lequel le serveur correspond à un plus grand nombre de clients. Le serveur et le client effectuent tous deux des microservices Web, le protocole de communication est HTTP, le format de données est JSON. Cette architecture est utile pour distribuer et redistribuer dynamiquement les ressources entre les clients. Un tel modèle architectural est utilisé pour créer de grandes applications complexes et évolutives horizontalement composées de petits processus indépendants et séparés qui interagissent les uns avec les autres à l'aide de l'API [21].

En figue. La figure 1 montre comment un serveur distribue des packages de fonctionnalités pour ses clients. Selon le nombre de clients, il peut y avoir des instructions qui ne seront attribuées à aucun client, ou le même ensemble d'instructions affecté à plusieurs clients.


Figure. 1. Distribution des services aux clients.

L'architecture de l'application a été construite à l'aide du cadre ASP.NET MVC de Microsoft. Dans la partie centrale, nous voyons des microservices de serveur sur le serveur lui-même, et à gauche et à droite, de nombreux clients attendent de démarrer des tâches à partir du serveur. Le composant de service d'orchestration fournit, d'une part, la communication entre le serveur et les clients, l'envoi de tâches aux clients, et d'autre part, il surveille l'état de ces demandes.

Cette architecture permet à un microservice d'appeler un autre microservice (ainsi nous obtenons un microservice étendu (étendu)) ou de s'appeler les uns les autres, ce qui peut conduire à une dépendance circulaire, qui devrait être évitée au niveau de l'utilisateur. Le protocole de communication client-serveur s'effectue selon les étapes suivantes:

  1. Le client se connecte au serveur et lance un protocole d'échange de clés. Il les fournira également au serveur et au port auxquels ils correspondront.
  2. Le serveur informe le client de la prochaine tâche à effectuer (la tâche est représentée par une paire (microservice, données d'entrée)).
  3. Le client reçoit le travail, puis informe le serveur que le transfert et le téléchargement se sont terminés avec succès ou sans succès.
  4. Dès que la connexion entre les deux objets est établie, le serveur envoie les données au format JSON, chiffrées à l'aide de DES, au client pour traitement.
  5. ( , ) JSON, DES.
  6. , .
  7. — .

Un cas particulier de cette interaction est un scénario où un client exécute une tâche pour laquelle le résultat d'un autre client est requis. Pour ce cas, deux possibilités existantes ont été évaluées: l'orchestration et la chorégraphie.

Dans le cas de la chorégraphie, nous avons identifié plusieurs obstacles: (a) une liste de clients disponibles pour effectuer une tâche externe devait être envoyée par le serveur au client, et le maintien de cette liste de valeurs mises à jour entraînerait souvent une charge accrue sur le réseau d'échange d'informations; (b) la communication entre les deux clients était vulnérable aux attaques. Deux situations ont été résolues grâce à l'orchestration. En fait, tous les soins de gestion incombent au serveur, et les clients ne sont que des objets simples avec lesquels il est facile de travailler.

Pour l'option de microservices Ă©tendus, les phases d'interaction client-client seront les suivantes:

  1. . , . , DES.
  2. , , . , , , , . , ( . .).
  3. , ( ), , .
  4. .
  5. Le client déchiffre le résultat avec un mot de passe à usage unique et poursuit l'exécution.

3.2. Application

Pour tester et évaluer cette architecture, nous avons implémenté plusieurs microservices que nous avons sollicités pour ce que nous voulions vérifier à la fois.


Figure. 2. L'interface.

Dans la première expérience, nous avons utilisé 3 microservices comme suit: (1) un microservice qui effectue une opération mathématique sur deux nombres (à l'aide de LibraryMath), (2) un microservice qui nous indique si le nombre est positif (MasterOfNumbers) et (3) un microservice étendu, qui appellera le premier microservice lorsqu'il recevra deux numéros, et le résultat sera envoyé au deuxième microservice pour extraire des informations sur ce numéro (UniverseOfSuperMath).

La figure 2 montre comment nous obtenons des calculs mathématiques à l'aide des microservices présentés. Au niveau de l'interface, seul le résultat d'une opération mathématique est affiché, le reste des informations peut être vu comme le résultat de la réception par le serveur d'un appel AJAX en appuyant sur la touche égale (les deux résultats sont positifs).

Ensuite, nous considérerons la fonctionnalité principale de l'application, qui se concentre sur ce qui se passe lorsqu'il y a un, deux ou plusieurs clients connectés. Dans la figure 3, nous voyons comment, dans nos expériences, nous avons lancé plus de clients sur l'ordinateur local, en utilisant différents ports pour chacun d'eux.


Figure. 3. L'interface.

Nous avons 6 champs: ClientToken - un jeton unique associé à chaque client (lorsque l'appel est local et a une valeur localhost); Date - le moment où la demande a été faite; IP & Port = adresse IP du client et le port par lequel la communication est établie; Fonction - nom de la fonction appelée; Succès - un indicateur booléen indiquant le succès de l'appel. Par exemple, nous remarquons que lors du premier appel (h: 8:38:21 le client n'est pas connecté au serveur, le processus est effectué par le serveur). Lors du deuxième appel, nous observons le comportement dynamique du système, dont l'une des tâches est effectuée par l'un des clients et les deux autres par le serveur. Plus précisément, UniverserOfSuperMath est appelé (localement - le client n'est pas disponible pour cette tâche), qui, à son tour, appelle deux autres microservices, un local et un via un client délégué pour utiliser une instruction spécifique, etc.ré.

Tolérance aux pannes
Une autre fonctionnalité que j'ai prise en compte lors de la création de cette architecture était liée à la tolérance aux pannes du système. Sur la base du scénario précédent, nous pouvons observer ce qui se passe si un ou plusieurs clients choisissent de quitter le système.

Dans la figure 3 à droite, l'appel à 8:46 illustre ce scénario. Les clients sur les ports 8390 et 8827 ont un problème local ou de réseau ou fermez simplement la connexion au serveur et le serveur ne reçoit pas de notification à temps pour les supprimer de la liste. Le serveur essaiera de contacter les clients et d'exécuter des commandes, mais s'ils ne répondent pas en temps opportun, le serveur assume leurs tâches et renvoie le résultat demandé. Pour confirmation, les clients seront à nouveau sollicités après un certain temps, et s'ils ne répondent toujours pas, ils seront supprimés de la liste des clients disponibles. Le prochain appel (8:47) ne demandera plus inutilement des clients qui ne sont plus disponibles et les tâches ignorées par les clients disponibles seront exécutées par le serveur.

Avantages et inconvénients de la solution proposée

Les avantages de cette architecture sont évidents: les faibles coûts d'hébergement, les microservices proposés dans un réseau distribué sont dynamiques et évolutifs automatiquement (lorsque les clients offrent également une puissance de calcul à mesure qu'ils augmentent, la puissance de calcul du système augmente).

Les limites doivent être soulignées également: lorsque la courbe de puissance de calcul ne correspond pas à la courbe de puissance client. Nous avons également une restriction sur la possibilité d'exécuter cette application sur n'importe quel système d'exploitation. Pour ce faire, nous avons décidé de convertir une solution abordable de .NET en Java. Mais cette solution présente certains inconvénients par rapport à la solution d'origine (Java offre une vitesse de traitement des données plus faible et un transfert de paquets moins dynamique que nous le faisons en .NET). Nous utilisons actuellement cette solution car .Net Core, proposé par Microsoft pour fonctionner sur plusieurs plateformes, n'est pas encore une solution mature et n'offre pas toutes les fonctionnalités de la plateforme .NET standard).

3.3. Composants client-serveur

3.3.1. Client

Dans cette architecture, le client est une application de bureau Windows Presentation Foundation (WPF) spécialement conçue pour communiquer avec le serveur et effectuer diverses tâches reçues de celui-ci. Étant donné que l'application est un fichier exécutable qui ne nécessite pas d'installation, le système d'exploitation doit fonctionner avec .Net Framework. Essentiellement, un microservice Web interagira avec un autre microservice Web.

Tout d'abord, le client démarre le planificateur de tâches dans un thread parallèle, qui chaque minute tentera de notifier le serveur de sa présence. Une tâche peut prendre deux états: (1) soit il y a une tâche à exécuter (l'initialisation du package de code est déjà terminée) - dans ce cas, elle ne notifie au serveur que sa présence; (2) ou nécessite une initialisation avec le serveur.

L'initialisation avec le serveur comprend, tout d'abord, un choix arbitraire de code et de port qui démarrera le serveur, qui à son tour lui seront envoyés en utilisant le protocole d'échange de clés Diffie-Hellman (IKE). Dès que la connexion entre les deux objets est établie, le serveur notifie au client un package d'instructions d'installation. Le rôle principal du client est de recevoir un package d'instructions du serveur, de le charger en mémoire, de traiter les informations reçues du serveur, puis de renvoyer le résultat obtenu en exécutant ce package d'instructions. La première étape effectuée par le client consiste à contacter le serveur pour un ensemble d'instructions. Ce paquet d'instructions se présente sous la forme d'une archive ZIP.

Avant d'extraire ce package, supprimez le répertoire précédent avec les instructions du dossier «process» (s'il existe), puis extrayez le nouveau contenu dans ce dossier et chargez-le en mémoire. Le chargement de la mémoire démarre une fois, quel que soit le nombre d'appels reçus par le client. Cela est possible car trois propriétés restent inchangées dans la session: assembly, methodInfo et type. L'assembly stocke un lien vers la DLL chargée, la propriété methodInfo contient la méthode appelée à partir de la DLL et type décrit le type de la DLL. Le fichier install.zip est un ensemble d'instructions reçues d'un serveur qui contient des DLL, du XML, des images, des fichiers de configuration, etc., et tout le code compilé qui sera exécuté dans un processus futur.

Cette étape marque le début de la communication entre le client et le serveur pour effectuer une tâche spécifique. Dès que le client est correctement initialisé pour effectuer une tâche spécifique, le serveur n'enverra que le paquet de données sous forme cryptée, qui doit être traité, et attendra également une réponse sous forme cryptée.

En exécutant le code reçu du serveur, le système est «verrouillé», le client peut se connecter à des bases de données, appeler d'autres API, en particulier, appeler d'autres clients qui exécutent des instructions identiques ou différentes. La connexion est établie dans le système d'orchestration, où le serveur recherche le prochain client disponible, demande le résultat et sa réponse est redirigée par le serveur vers le client. Cette orchestration de microservices est appelée «ExtendedService», et la seule différence au niveau du client est que le chiffrement est optimisé.

Le problème technique était de réinitialiser le client avec un autre paquet d'instructions à exécuter. Étant donné que le chargement de la mémoire est statique dans un contexte spécial (serveur Web), cela n'a été possible qu'en redémarrant l'ensemble du processus pour traiter les DLL chargées en mémoire. Pour ce faire, nous avons créé des événements dans Windows que nous exécutons à partir d'une application Web exécutée dans une application de bureau. Cela est nécessaire car nous avons affaire à deux contextes différents dans deux threads d'exécution différents.

3.3.2. Serveur

Le microservice intégré a une interface ILibraryMath, qui fournit la méthode SimpleMath, et l'interface est implémentée par la classe LibraryMath. La classe LibraryMath étend la classe abstraite universelle MicroCore, qui a deux paramètres correspondants pour l'entrée et la sortie. En étendant cette classe abstraite, la méthode ProcessTask doit être implémentée là où tout le code à exécuter est écrit, et la fonction Run est appelée dans la classe abstraite étendue pour exécuter ce code dans la méthode SimpleMath. Ainsi, il est possible de définir des interfaces et des méthodes, non limitées à un nom spécifique, mais, en passant le code à travers une classe abstraite, nous obtiendrons un contrôle total sur le code, que nous pouvons distribuer entre différents clients. Dans cette classe, nous pouvons facilement avoir plus de fonctions et de bibliothèques importées,s'ils sont regroupés dans un seul package.

L'étape suivante consiste à écrire cette interface dans SimpleInjector, une bibliothèque qui facilite le déploiement d'un modèle d'injection de dépendance avec des composants à couplage lâche. En plus d'enregistrer des classes entrelacées dans le conteneur Simple Injector, afin de rompre la dépendance entre les niveaux d'application (introduction de dépendances de modèle), nous devons enregistrer la classe dans le conteneur de stockage de microservices, qui sera mis à l'échelle par l'application. Après cette étape, nous pourrons utiliser la fonction fournie par l'interface pour le but créé.

Service1 implémente IService1 et étend la classe MicroCore abstraite, puis s'inscrit auprès de MicroContainer.RegisterMicro dans ce conteneur. Il convient de mentionner l'existence d'API disponibles dans localohst / DynamicMicros / {Service} via lesquelles les clients communiquent avec le serveur. Actions importantes disponibles via ces API: le client se connecte, le client informe le serveur de son activité, les microservices se développent, etc. Ensuite, nous présentons les classes MicroCore et MicroContainer, qui forment ensemble la base de notre application.

La classe MicroCore est une classe abstraite et universelle et est responsable de l'appel de code à partir de la méthode virtuelle ProcessTask. Cela se fait en appelant la méthode Run, qui à son tour appelle la méthode publique TaskManager. Notez que le microservice, à son tour, appellera également cette méthode. Lorsqu'un package ZIP est envoyé au client pour être chargé en mémoire et exécuté, il est envoyé avec toutes ses dépendances, y compris cette classe, qui est utilisée pour gérer le microservice du client. Le contrôle d'exécution comprend la désérialisation / sérialisation du paquet de données à envoyer, l'appel du code lui-même, l'appel d'autres API, etc.

Revenant du côté serveur, le contrôle de l'exécution du code comprend les étapes suivantes:

  1. S'il s'agit d'un appel ExtendedService, le serveur sera appelé pour répondre.
  2. Si un client est disponible pour la demande, celle-ci lui sera envoyée pour traiter le résultat; dans le cas négatif, le serveur lui-même traitera les données.
  3. Nous demandons un client pour le traitement des données.
  4. Si le client a des problèmes, nous demandons à nouveau la confirmation de la disponibilité, mais envoyons une réponse du serveur (pour éviter les temps d'arrêt et les longs délais d'attente).
  5. Nous enregistrons l'activité en cours.

La classe MicroContainer est l'espace de gestion de l'ensemble du microsystème intégré. Ici, les clients qui connectent l'application (serveur) se connectent, et il existe des appels de fonction qui étendent la classe abstraite MicroCore pour les «services avancés». Il s'agit d'une classe statique dans laquelle la liste des tâches effectuées sur les microservices, la liste des clients connectés et la liste des tâches client qui effectuent ces tâches sont stockées dans le dictionnaire.

Une fois lancée, la classe sera enregistrée pour intégration dans le microservice à l'aide de RegisterMicro. Cela ne se produira qu'une seule fois lors de l'initialisation. La méthode AddNewClient nous fournit l'enregistrement d'un nouveau client, l'échange de clés, l'enregistrement de l'adresse IP du serveur et le port sur lequel il fonctionnera. Le jeton reçu par le nouveau client sera vérifié avant d'être inséré dans la liste des clients pour confirmer son unicité. Une fois la connexion avec le client établie, le serveur appellera la méthode InstallService, qui compresse les données, les envoie et après que le client a répondu, elles seront ajoutées au dictionnaire pour cette tâche. Le temps de service qui sera alloué à chaque client dépend de la stratégie utilisée. Lorsque vous démarrez le microservice abstrait MicroCore, appelé à la fois sur le serveur et sur le client (avec ExtendedService),une demande est faite pour les clients disponibles pour la tâche demandée à l'aide de la fonction GetNextClient. Cette opération sera effectuée très souvent et sa complexité affectera directement le temps de réponse de l'application. C'est pourquoi notre approche était de sélectionner au hasard un client. Cela se fait rapidement et à partir de nos expériences assure une distribution uniforme des appels.

Une autre option était d'implémenter une liste de tourniquet - une solution qui a l'inconvénient que dans le cas d'un flux d'E / S client important, la mise à jour de la liste de tourniquet nécessitera plus de temps et de complexité, ce que nous avons essayé d'éviter. La méthode RecordClientError est appelée lorsque le client ne répond pas à la demande reçue. Après avoir répondu à cette question, une décision est prise pour enregistrer ou supprimer ce client. Les clients sont identifiés de manière unique par le code de jeton envoyé par le client lors de l'initialisation, et chaque microservice est identifié par un espace de noms et un nom de classe. Toutes les ressources (clients, code) sont gérées par cette unité unitaire qui assure le support des opérations nécessaires.

En ce qui concerne la sécurité du système, des mesures ont été prises pour empêcher les attaques, les interceptions et la protection des données. Tous les messages envoyés entre le serveur et les clients sont chiffrés à l'aide de l'algorithme de clé DES symétrique et de l'échange de clés Diffie-Hellman entre le client et le serveur, qui se produit lors de l'initialisation du client. Les clients disponibles et les programmes en cours d'exécution sont stockés dans la mémoire du serveur. Nous avons choisi cette solution car, à notre avis, c'était la meilleure option, car elle fournit un accès haut débit aux données, les informations peuvent changer très souvent et la zone mémoire est très difficile à attaquer.

3.4. Comportement dynamique d'un système de microservices

Tout d'abord, tous les ordinateurs sur lesquels les clients travailleront peuvent être sur le même réseau ou sur des réseaux différents. Deux éléments sont prioritaires: a) le temps consacré au transfert de données; et (b) les frais généraux ajoutés par le système pour la gestion des données (par exemple, recherche de clients, chiffrement, déchiffrement, traitement des erreurs, etc.). Nous nous sommes principalement intéressés au comportement de notre système dans les réseaux locaux (LAN) et mondiaux (WAN) (Fig. 4).


Figure. 4. Enregistrement du système fonctionnant dans un réseau local (la première colonne de journaux) et global (la deuxième colonne de journaux).

La colonne Nom de la tâche contient toutes les inscriptions effectuées par l'appel client pour chaque tâche, et les colonnes Journaux sont les heures et la durée en ms pour chaque traitement de tâche (à gauche dans le réseau local et à droite dans le réseau global). Notez que les tâches ont le temps de réponse le plus long au premier appel, après quoi il diminue. Naturellement, car tous les téléchargements de mémoire, les adresses de sauvegarde, etc. sont généralement effectués lors du premier appel. Les trois premières tâches sont de simples opérations mathématiques qui sont généralement effectuées en quelques millisecondes - le temps qui est également requis pour notre système.

Pour un réseau local, nous avons en moyenne 20 à 30 millisecondes par tâche, ce qui provient du chiffrement, de la journalisation et de la transmission sur le réseau (même s'il est local). Ce modèle de communication LAN est également utilisé dans le cloud, où les ordinateurs sont situés au même endroit (centre de données), et la communication entre eux se fait via la fibre optique, le retard du réseau est minime. Les résultats sont présentés sur la fig. 4 dans la colonne de gauche des journaux.

Pour tester notre application WAN, nous avons configuré le routeur pour acheminer un appel du port 80 vers:http://192.168.1.160/(adresse réseau) et IIS (Internet Information Services) ont lancé l'application et elle était accessible de n'importe où en dehors du réseau local. Pour exécuter l'application au niveau du client, le droit d'utiliser les ports 8000: 9000 (ports arbitraires) était requis. Les clients sont disposés à des points arbitraires, la connexion à l'adresse IP publique via l' API été établie: https://api.ipify.org/. Les résultats sont présentés sur la fig. 4 dans la colonne du journal à droite.

Dans les résultats présentés sur la fig. 4, les valeurs dans la colonne de droite du journal sont 16 à 17% supérieures aux valeurs de la colonne de gauche du journal pour les trois premières tâches (sans communication avec d'autres microservices) et ± 10% pour les microservices qui ont téléchargé des documents à partir d'Internet ou interagi avec la base de données sur serveur spécifique.

4. Évaluation


Dans cette étude, nous avons surveillé le comportement du système à la fois dans le réseau local (connexion de 5 ordinateurs via un réseau sans fil) et dans le réseau mondial (en utilisant l' espace de noms mihaidm.ddns.net ), en comparant notre système avec un système monolithique, ces opérations sont effectuées sur le même ordinateur (voir tableau 1).

Tableau 1. Évaluation du système pour les réseaux.
calcul (ms)écrire dans la base de données (ms)génération de pdf (ms)
localhost14,45815.449
Lan254.40816.415
blĂŞme544.82629.309


Les tests ont été effectués séquentiellement sur un appareil avec 5 clients connectés pour les tests de réseau. Chaque tâche a été exécutée 100 fois, évaluant le nombre total de millisecondes dans tous les appels.

C'était un produit de deux nombres comme calcul numérique. Un microservice n'interagit pas avec d'autres microservices, la quantité d'informations transmises sur le réseau est faible et la complexité est minimisée pour étudier strictement le temps passé sur les tâches de gestion du serveur, du client et du réseau. Si le calcul est effectué par le serveur (localhost), il est d'abord vérifié s'il y a un client disponible, et puisque le client n'est pas connecté, le serveur traite le résultat. Dans le cas suivant, la présence de clients sur le réseau local montre la fin de la tâche dans des conditions de fonctionnement très rapide du réseau, et du côté traitement, chiffrement / déchiffrement, recherche de la réponse du client. Pour 100 exécutions, le temps moyen nécessaire pour terminer l'opération était de 25 ms, ce qui est une valeur prometteuse compte tenu du rapport flexibilité / vitesse. En cas de WAN, le temps est deux fois plus longque dans le réseau local (54 ms), cela est dû au processus de chiffrement, aux coûts de transport, mais pour l'exécution réelle, il faut une demi-milliseconde.

Une autre tâche que nous avons étudiée consiste à écrire dans la base de données. En particulier, le mot qui sera écrit dans la base de données est pris comme paramètre. Nous souhaitons savoir à quelle vitesse le client contactera une base de données située en dehors de la zone locale (pour cette étude, la base de données était située sur www.my.gearhost.com ). Notez que les valeurs d'exécution sur LAN et localhost sont proches. Dans le réseau mondial, la différence est notable, car le traitement, la gestion des données et des clients ne prennent pas autant de temps que la plage de clients qui se connecte à la base de données pour insérer la valeur.

La dernière tâche réalisée dans cette étude a été la création d'un fichier PDF, notre objectif était d'estimer le temps de transmission des données dans le système. Pour ce faire, nous téléchargeons le fichier PDF sur www.pdf-archive.com/2018/05/14/diploma/diploma.pdf , qui est chargé en mémoire. Le système écrit le nom à une position spécifique et renvoie le résultat (sous la forme de vecteurs d'octets) au serveur. Pour un hôte local et un réseau local, une différence d'environ 1 000 ms représente le temps requis pour chiffrer et transférer des fichiers PDF localement. Pour le WAN, la valeur résultante est plus élevée car le coût de transmission du vecteur d'octets est très élevé.

5. Conclusions et travaux futurs


La nature générale et abstraite de l'architecture du système, présentée dans ce travail côté serveur, a rendu la conception difficile, car le même code est exécuté à la fois par le serveur et le client. Nous pouvons affirmer que l'architecture actuelle est compacte, simple, facile à comprendre et à étendre; le client peut effectuer les tâches assignées par le serveur, le serveur est un monolithe et l'interface client.

L'architecture proposée permet de créer très facilement de nouveaux microservices, qui sont ensuite automatiquement intégrés au système embarqué. Éléments innovants de cette architecture: évolutive très facilement, chaque nouveau client reçoit une tâche du serveur en fonction de la stratégie poursuivie (les tâches les plus chères, les plus courantes, une combinaison des deux précédemment listées ou simplement une stratégie arbitraire). En fait, nous avons un monolithe avec la flexibilité d'un système de microservices. Le serveur traite la distribution dynamique des tâches entre les clients, fournissant une mise à l'échelle dynamique basée sur un certain nombre de paramètres (le nombre d'appels à la tâche, son temps d'exécution ou une combinaison de ceux-ci).

L'une des orientations futures tient compte du fait que ce système peut être intégré avec succès dans un site Web ou un système API avec un caractère applicatif prononcé. L'architecture proposée peut être améliorée et étendue à tout moment en raison de la disponibilité de plusieurs plates-formes (par exemple, pour les téléphones mobiles).

Une autre direction à l'avenir que nous envisageons est considérée comme extrêmement intéressante aujourd'hui - est que l'utilisateur fournit une puissance de calcul en échange d'une redevance (par exemple, le système BITCOIN), notre application est développée pour exécuter des microservices sur certains ordinateurs.

Lien source


Cette étude a été publiée avec le soutien du programme POC-A1-A1.2.3-G-2015, dans le cadre du projet PrivateSky (P_40_371 / 13/01/01/2016) et du projet README «Application interactive et innovante pour évaluer la lisibilité des textes roumains et améliorer l'utilisateur styles d'écriture », contrat n ° 114 / 09.15.2017, MySMIS 2014 code 119286.

Références


[1] Dragoni, N., Giallorenzo, S., Lluch-Lafuente, AL, Mazzara, M., Montesi, F., Mustafin, R. (2017a) "Microservices: hier, aujourd'hui et demain". Mazzara M., Meyer B. (Ă©d.), Present and Ulterior Software Engineering. Springer
[2] Mazzara, M., Khanda, K., Mustafin, R., Rivera, V., Safina, L. et Silitti, A. (2018) «Microservices Science and Engineering». Dans: P. Ciancarini, S.Litvinov, A. Messina, A., Sillitti, G. Succi (éd.) Actes de la 5e Conférence internationale en génie logiciel pour les applications de défense, SEDA 2016, Springer, 10-20.
[3] Dragoni, N., Lanese, I., Larsen, ST, Mazzara, M., Mustafin, R. et Safina, L. (2017b) «Microservices: How To Make Your Application Scale». Dans: Petrenko A., Voronkov A. (éd.) Perspectives of System Informatics. PSI 2017. Notes de cours en informatique, 10742. Springer, Cham.
[4] Melis, A., Mirri, S., Prandi, C., Prandini, M., Salomoni, P. et Callegati, F. (2016) «Un cas d'utilisation de l'architecture de microservices pour les personnes handicapées». À la 2e conférence internationale EAI sur les objets intelligents et les technologies pour le bien social, DOI: 10.1007 / 978-3-319-61949-1_5.
[5] Zimmermann, O. (2017) «Micro-services Tenets: Agile Approach to Service Development and Deployment, Computer Science - Research and Development», 32 (3-4): 301-310.
[6] Xia, C., Zhang, Y., Wang, L, Coleman, S. et Liu, Y. (2018) «Système de robotique en nuage basé sur les microservices pour l'espace intelligent». Dans: Robotics and Autonomous Systems 110, DOI: 10.1016 / j.robot.2018.10.001.
[7] Bogner, J., Fritzsch, J., Wagner, S. et Zimmermann, A. (2019) «Microservices in Industry: Insights in Technologies, Characteristics and Software Quality». Lors de la conférence internationale IEEE 2019 sur les ateliers d'architecture logicielle (ICSAW) à: Hambourg, Allemagne.
[8] Akentev, E., Tchitchigin, A., Safina, L. et Mzzara, M. (2017) «Vérificateur de type vérifié pour le langage de programmation Jolie», https: // arXiv.org/pdf/1703.05186.pdf.
[9] Černý, T., Donahoo, MJ et Trnka, M. (2018) «Compréhension contextuelle de l'architecture des microservices: orientations actuelles et futures». ACM SIGAPP Applied Computing Review 17 (4): 29-45, DOI: 10.1145 / 3183628.3183631.
[10] Larucces, X., Santamaria, I., Colomo-Palacios, R. et Ebert, C. (2018) «Microservices». Dans: IEEE Software, 35/3: 96-100.
[11] Kalske, M. (2017) «Transformer l'architecture monolithique vers l'architecture de microservices». M.Sc. Thèse, Univ. d'Helsinki.
[12] Lenarduzzi, V., et Taibi, D. (2016) «MVP Explained: A Systematic Mapping Study on the Definitions of Minimal Viable Product». Lors de la 42e conférence Euromicro sur le génie logiciel et les applications avancées (SEAA), 112-119.
[13] Taibi, D., Lenarduzzi, V., Janes, A., Liukkunen, K. et Ahmad, MO (2017) «Comparing Requirements Decomposition within the Scrum, Scrum with Kanban, XP, and Banana Development Process». Dans: Baumeister H., Lichter H., Riebisch M. (éd.) Agile Processes in Software Engineering and Extreme Programming. Notes de cours sur le traitement de l'information commerciale, 283. Springer, Cham.
[14] Gómez, A., Benelallam, A. et Tisi, M. (2015) «Persistance du modèle décentralisé pour le calcul distribué». Au 3e atelier BigMDE, L'Aquila, Italie.
[15] Kandave, KR (2018) «Calcul haute performance sur Azure». Nanette Ray (éd.), AzureCAT, Microsoft Corporation.
[16] Sreenivas, V., SriHarsha, S. et Narasimham, C. (2012) «Un modèle cloud pour mettre en œuvre le SaaS». Dans: Advanced Materials Research 341-342, Trans Tech Publications, Suisse, 499-503.
[17] Badidi, E. (2013) «A Framework for Software-As-A-Service Selection and Provisioning». Dans: International Journal of Computer Networks & Communications (IJCNC), 5 (3): 189-200.
[18] Lynn, T., Rosati, P., Lejeune, A. et Emeakaroha, V. (2017) «A Preliminary Review of Enterprise Serverless Cloud Computing (Functionas-a-Service) Platforms». Lors de la 9e conférence internationale IEEE 2017 sur les technologies et les sciences du cloud computing, 162-169.
[19] Adzic, G. et Chatley, R. (2017) «Serverless Computing: Economic and Architectural Impact». À: ESEC / FSE'17, 4-8 septembre 2017, Paderborn, Allemagne, ACM.
[20] Diffie, W. et Hellman, M. (1976) «New directions in cryptography». Dans: IEEE Transactions on, Information Theory, 22 (6): 644–654.
[21] Kratzke, N. (2015) «À propos des microservices, des conteneurs et de leur impact sous-estimé sur les performances du réseau». Au CLOUD Comput. 2015, 180 arxiv.org/abs/1710.04049 .

En savoir plus sur le cours

All Articles