Architecture microservice, spring-boot: De la théorie à la pratique (Part 2)

spring&microservices

Cet article est la seconde partie du premier sur le thème d'architecture micro-services (AMS ou MSA). Dans cette partie, comme promis, nous passons à la pratique.

Rappelons à nouveau les inconvénients d'une application monolithique traditionnelle (tous les services dans une seule archive, .war par exemple):

  • L'application monolithique est tellement grosse que la correction d'un simple bug devient consommatrice de temps,
  • Impossible pour un développeur de comprendre cette application réalisée durant plusieurs années,
  • La difficulté de changer quoique ce soit dans une application monolithique. Ajouter une nouvelle fonctionnalité devient risqué !
  • Certaines applications monolithiques nécessitent plusieurs minutes pour être lancées, ainsi beaucoup de temps sert à relancer afin de les déboguer,
  • La fiabilité de l'application monolithique est faible car si un des services est down c'est toute l'application qui est down puisque tous les services sont exécutés dans le même processus. On peut illustrer ce point avec ce scénario : imaginez un site e-commerce devant afficher les produits et les recommandations associées. Un développeur lambda réalise une méthode du service qui va chercher la liste des produits et la liste des recommandations de chaque produit. Mais s'il n'arrive pas à récupérer, pour un produit quelconque, sa liste de recommandations, il décide (forcé?) d'afficher une page d'erreur ! Alors qu'il était possible et raisonnable de juste afficher la liste des produits même sans les recommandations.
  • Dans une application monolithique difficile de définir l'axe à retenir pour rendre l'application "scalable" :  C'est à dire, imaginons qu'un des services a besoin d'augmenter le CPU pour effectuer des calculs or le service voisin n'a pas du tout ce besoin mais comme il partage la même configuration machine, difficile de trouver un compromis. Alors la puissance CPU pour toute l'application et c'est parti.

Rappelons aussi que l'objectif principal de l'AMS est de rendre agile le développement et le déploiement.

Mais cette architecture vient avec son lot d'inconvénients :

  • L'inconvénient majeur de micro-services est de croire que, de part son nom, le découpage en micro services est en soit l'objectif final.
  • L'autre inconvénient majeur est de maîtriser la complexité liée à la communication, dans un système distribué, entre ces services.
  • Un autre inconvénient ou défi à relever est de revoir l’architecture de la partie base de données (back BDD).
    Jusqu'ici la solution de gérer les transactions métier de plusieurs entités est presque devenue archiconnue lorsqu'une seule base est définie. Mais avec l'architecture micro-services, il faudrait gérer plusieurs bases de données car chaque service peut avoir besoin de sa propre base de données.
    Penser aux transactions distribuées pour gérer la multiplicité des bases ne peut pas être l'option la plus facile à retenir.

Nous insistons un peu : dans une architecture AMS, le focus doit être fait non seulement sur le découpage des services mais aussi et surtout sur les interactions entre ces services.

Aussi, nous devons éviter d'appeler un service directement via sa location (même si elle est configurée selon la plateforme cible de déploiement).

Par exemple, évitez d'utiliser l'url: http[s]://[host]:[port]/[nameservice] afin ne pas créer un couplage fort entre les services.

Lorsque les services sont déployés et exécutés dans un système distribué (chaque instance est souvent une VM (cloud) ou encore un container Docker). Par conséquent, nous ne savons rien sur les ip ou les dns des machines qui hébergent ces services. D'où l'intérêt du "Service Discovery".

Pour bien démarrer la partie pratique et afin de faciliter la prise en main, j'ai décidé d'isoler les difficultés et de simplifier les notions abordées en procédant par étape.

Nous commençons donc par le thème des interactions entre services ou encore en un mot "Service Discovery". On distinguera le "Server-Side Dsicovery" du "Client-Side Discovery".

Pour en savoir plus sur la notion de "Service Discovery" voir ici.

Comme vous pouvez le constater plus loin, nous avons volontairement choisi l'approche où c'est le client qui s'enregistre dans le "Service Discovery". L'inconvénient de cette approche est de coder cette partie dans chaque service client. Mais avec l'API Spring la tâche est tellement facile.

Il y a certainement d'autres approches possibles exemple un "registrar" qui centralise l'enregistrement des services sans impact sur le code du service client.

Voici le sommaire de ce que nous allons voir, décomposé en deux parties distinctes :

Service Discovery Registry (side server)

  • Créer un projet spring-boot afin de centraliser et d'enregistrer les services,
    La solution open-source, Eureka, de Netflix et l'API Spring-Cloud rendront cette entreprise facile.
  • Compléter ou vérifier le pom projet,
  • Annoter la classe générée par spring-boot,
  • Configurer service Eureka Registry.

Service Discovery Client (client side)

  • Créer un second projet spring-boot définissant le micro-service qui sera déployé dans le serveur Eureka.
    Tout cela est réalisé de manière automatisée grâce encore à l'API Spring,
  • Compléter ou vérifier le pom projet,
  • Annoter la classe générée par spring-boot,
  • Ecrire un RestController pour illustrer les notions de consommation de services via leurs noms enregistrés dans "Eureka Registry".

Les ingrédients de la recette de notre démo sont:

  • Entrée: Spring-boot,
  • Plat: API Eureka de Netflix & API Spring-cloud,
  • Dessert: Rest Spring,
  • Java 8 pour ingrédient de base.

Merci de noter que je me suis servi librement du super article de Josh Long sur le thème de micro-services.

PARTIE 1. Service Eureka Discovery / Registry

La réalisation de cette partie peut être décomposée en plusieurs actes:

ACTE 1. Créer le projet spring-boot

Nous partons de la page Spring initilzr pour créer notre projet.

Vous voyez sur la capture ci-dessous que la seule dépendance choisie est "Eureka Discovery". La version 1.3.3 de Spring-Boot a été sélectionnée.

 

blog_spring_initilzr_step1

 

 

ACTE 2. Vérifier le pom

L'action réalisée à l'étape précédente doit générer ce pom (extrait):

<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.3.3.RELEASE</version>
		<relativePath/> 
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
	
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka-server</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
pom

 

Vous constatez la présence de la seule dépendance intéressante "spring-cloud-starter-eureka-server" déjà évoquée précédemment.

Vous avez aussi certainement noté la présence de la classe java nommée DemoApplication.java (si vous n'avez pas changé le nom de l'artifact dans l'écran initilizr).

 

ACTE 3. Annoter la classe générée

La classe DemoApplication est générée et annotée avec @SpringBootApplication de Spring-Boot, nous l'annotons encore avec @EnableEurekaServer de spring-cloud (package org.springframework.cloud.netflix.eureka.server.EnableEurekaServer).

Faisant cela, nous avons utilisé la brique de Netflix nommée Eureka qui est un "service discovery ou service registry".

L'annotation @EnableEurekaServer est "side server", fournie par l'api spring-cloud pour faciliter la mise en place de la découverte du service Eureka.

Avec l'acte suivant, nous complétons la configuration du service Eureka via le fichier properties ou yaml.

 

ACTE 4. Configurer Eureka

Le fichier application.(properties, yml)  est généré par spring-boot.

Nous avons retenu le fichier YAML nommé application.yml dont le contenu est comme suit:

server:
  port: 8761

eureka:
  instance:
    hostname: localhost
  client:
    fetchRegistry: false
    registerWithEureka: false
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
application.yml

ACTE 5. Tester le service Eureka

Il suffit d'exécuter la classe ( clic-droit -> run as -> spring-boot app) puis de saisir dans le browser l'url localhost:8761 pour voir s'afficher la page du service Eureka.

Nous obtenons cette capture:

Service_eureka_init

A ce stade aucune instance de micro-service n'est enregistrée d'où la mention "No instances available".

Je crois que la longueur de cet article m'oblige à remettre le reste à la prochaine partie, dans laquelle, nous enregistrons un premier micro-service client dans le "Service Eureka Discovery". Puis un second service...

Voici un aperçu général des actes que nous aborderons :

 

PARTIE 2. Service Client Eureka

ACTE 1. Créer le second projet Spring-boot

ACTE 2. Vérifier le pom projet

ACTE 3. Annoter la classe générée

ACTE 4. Configurer Client Eureka

ACTE 5. RestController pour consommer les micro-services

 

A très vite,

6 commentaires

  1. Merci pour l’article !
    Nous testons de notre côté Vert.x, ainsi que Dropwizard. As-tu un retour d’expérience à ce sujet ?

    Can

  2. Merci pour ce retour, ça fait toujours plaisir 🙂

    Désolé je n’ai pas utilisé ni Dropwizard (je préfère spring-rest & spring-boot) ni Vert.x.

    Abdou,

  3. Bonjour,
    votre article reste inachevé ou vous avez publiez les parties manquantes de « Architecture microservice, spring-boot.. »?. Dans ce cas, je serai ravie de vous lire.
    merci pour cet article en attendant la suite…

  4. Bonjour,

    Merci pour votre commentaire.
    Le troisième volet est en cours de rédaction, il sera publié très prochainement.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.