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

spring&microservices

Cet article est la troisième partie qui complète le second article sur le thème d’architecture micro-services (AMS ou MSA).

Dans cette troisième partie, comme promis, nous écrierons un (voire deux) micro-service(s) qui s'auto-enregistre(nt) dans l'annuaire Eureka ("Service Eureka Discovery"), le focus est mis sur les interactions des micro-services. Vous verrez que les appels entre les différents services se font via leurs noms d'enregistrement dans eureka sans avoir à connaître ni le nom dns ni le host ni le n° de port.

PRE-REQUIS/!\

Un pré-requis impératif est d'avoir parcouru la seconde partie et de penser à lancer le registry eureka avant d'exécuter le micro-service client ci-après.

Nous suivons ce plan, divisé en 5 actes, déjà annoncé dans le précédent article :

  1. ACTE 1. Créer le second projet Spring-boot
  2. ACTE 2. Vérifier le pom projet
  3. ACTE 3. Annoter la classe générée
  4. ACTE 4. Configurer Client Eureka
  5. ACTE 5. RestController et un test JUnit d'intégration pour consommer les micro-services

Mettons la main à la pâte avec les ingrédients java 8, spring-boot, spring-cloud combinés avec l'API Eureka de Netflix.

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

Nous partons toujours de la page Initilizr de spring.io pour créer une application Web & Rest.

ACTE 2. Vérifier le pom projet

Assurez-vous de compléter avec la dépendance suivante nécessaire (starter-web en ayant coché ou choisi ce starter dans la page initilizr) :

<dependency>
		<dependency>
		  <groupId>org.springframework.boot</groupId>
		  <artifactId>spring-boot-starter-web</artifactId>
		</dependency>
pom

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

La classe Main générée par spring-boot va être annotée avec @EnableEurekaClient de l'API spring-cloud pour Netflix comme suit :

package xxxxxxxxxxxx;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class ServiceClientEureka {
    public static void main(String[] args) {
        SpringApplication.run(ServiceClientEureka.class, args);
    }
Main de SB

Pensez à adapter certains passages (le nom du package, le nom de la classe main générée par spring-boot,..).

A quoi sert l'annotation @EnableEurekaClient ?

L'annotation @EnableEurekaClient va garantir que le service s'enregistre automatiquement dans le registry eureka. C'est aussi simple que cela! Juste une annotation apposée sur la classe main générée. L'étape suivante va permettre de compléter la configuration utile pour l'auto-enregistrement dans le service discovery eureka.

ACTE 4. Configurer Client Eureka

Le fichier de configuration de ce service est nommé application.yml :

spring:
  application:
    name: service-client-eureka

server:
  port: 8081

eureka:
  client:
    serviceUrl:
    defaultZone: http://localhost:8761/eureka/
    
  instance:
    leaseRenewalIntervalInSeconds: 10
    metadataMap:
     instanceId: ${spring.application.name}:${server.port}/
application.yml

 

Les points importants dans ce fichier .yml :

  • Le host du registry eureka précisé via defaultZone: http://localhost:8761/eureka/,
  • Le name: service-client-eureka sera utilisé pour auto-enregistrer le service avec ce nom dans eureka registry,
  • Le port pour la webapp est fixé à 8081,
  • Les informations de l'instance sont optionnelles. Vous verrez néanmoins dans la capture ci-après un lien correspondant justement à la valeur instanceId.

NOTE. Si le registry est déployé sur un (ou plusieurs) container(s) docker (c'est ce que nous ferons prochainement), il faudrait alors indiquer l'ip ou les ip du (des) container(s) docker séparés avec une virgule.

Optionnellement, vous pourriez compléter ce fichier application.yml pour configurer les traces logs comme suit :

logging:
  level:
    root: WARN
    com.netflix.discovery: WARN
    [NOM_PACKAGE_XXXX]: INFO 
suite application.yml

Adaptez le nom du package souhaité et le niveau de log désiré.

ACTE 5. RestController et test d'intégration pour consommer les micro-services

Cette partie est très dense et est la partie la plus intéressante de cet article car elle présente deux façons de découvrir les appels aux micro-services :

  1.  L'API discovery client qui permet d'interroger le server discovery à l'aide du nom du service et d'enchaîner les appels des méthodes ou uri disponibles,
  2.  En utilisant RestTemplate en lui passant comme argument une URI composée du nom du service du style : http://[NOM_SERVICE_ENREGISTRE]/[methodeAappeler];
    par exemple :http://service-client-eureka/infos

Dans l'acte 5.1 ci-dessous, nous mettrons en place les deux manières à des fins pédagogiques.

Dans le test Junit d'intégration ci-dessous, nous utilisons plutôt RestTemplate avec le nom du service nommé dans notre cas "service-client-eureka".

  • ACTE 5.1

    Ecrivons un peu de code pour RestController :

package xxxxxxxxxxx;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class RestControllerClientEureka {
    @Autowired
    private DiscoveryClient discoveryClient;	
    @Autowired RestTemplate restTemplate;
    @RequestMapping("/infosDiscoveryClient")
    String infos() throws Exception{
    	final StringBuilder sb=new StringBuilder();
    	final DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.SHORT,Locale.FRANCE);
    	discoveryClient.getInstances("service-client-eureka")
    			.forEach( 
    			  si ->
    			   {   
                             sb.append(si.getServiceId()); 
                             sb.append(" uri="); 
                             sb.append(si.getUri());
    			   }
    			);
		return  "<h2>Infos (à "+ df.format(new Date()) + ") : "+sb.toString()+"</h2>\n";
	}
    
    @RequestMapping({"/infos","/"}) 
    String restInfos() throws Exception{
	 final String retour = restTemplate.getForObject("http://service-client-eureka/infosDiscoveryClient",String.class);
         
         return retour;
   }
}
RestController

Encore, il faudrait modifier le nom de package.

A quoi sert L'attribut DiscoveryClient ?

L'attribut autowired discoveryClient offre des méthodes d'interrogation du registry eureka pour découvrir un service via son nom. Dans notre cas, un simple discoveryClient .getInstances("service-client-eureka") renvoie la liste des instances présentes dans le registry.

Avec forEach, pour chaque instance, nous extrayons quelques infos utiles à afficher.

  • ACTE 5.2

    Ecrire un test Junit d'intégration :

package monpremiermicroservice;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {ServiceClientEurekaTests.class, RestTemplate.class})

public class ServiceClientEurekaTests {
	
    @Autowired RestTemplate restTemplate;
    @Test
    public void dicoverEurekaTest() throws Exception {
    	
    	final String retour = restTemplate.getForObject("http://service-client-eureka/infosDiscoveryClient",String.class);
        //Assert...
    }
}
Junit

Rien de nouveau ici puisque nous nous appuyons sur RestTemplate de spring pour passer une URI composé du nom du service dans le registry.

CONCLUSION

Notre objectif initial de faire le focus sur les interactions entre micro-services est atteint avec le registry eureka qui peut-être bien évidemment remplacé par une solution open-source nommée  telle "consul" ou zookeeper.

Les différents micro-services créés sont simples mais permettent justement d'aller plus loin dans l'architecture micro-services.

Nous reviendrons plus loin sur ce thème en déployant, de manière isolée, les différentes briques (registry eureka, chacun des services) dans des dockers.

@Enjoy,

4 commentaires

  1. J’ai 2 micro-services , le principe est que lorsque un utilisateur veut s’inscrire au micro-service2 , il faut que ce utilisateur a un compte dans le micro service1 ,mon probleme est que comment je peut récupérer les enregistrements de micros-service 1 dans le micros-service 2 via Eureka

  2. Excellent article.
    J’ai découvert JHIPSTER il y a peu, et ils ont un projet open-source à part, JHipster Registry, qui utilise Eureka/Springboot/Zuul/… c’est plutôt pas mal. Vous devriez jeter un oeil.

Laisser un commentaire

Votre adresse e-mail 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.