Ajoutez vos propres configurations
Quand vous développez un microservice, vous avez souvent besoin de définir certaines propriétés ou valeurs fixes qui conditionnent le fonctionnement de votre application. Par exemple : le port d'écoute d'un microservice, une clé secrète d'accès à des ressources protégées, ou encore des valeurs liées aux langues et dictionnaires utilisés.
Pourquoi ne pas mettre tout simplement des constantes dans le code ?
Cette solution est à éviter, car quand vous avez un microcommerce avec énormément de valeurs fixes à renseigner, il devient complexe de les retrouver dans les dizaines de classes de votre microservice.
Pire encore, quand vous souhaitez changer une constante, il faut arrêter le microservice et le mettre à jour avant de le redéployer.
La solution est de regrouper toutes vos constantes de configuration dans un fichier que nous avons depuis le début : application.properties .
Pour prendre un exemple, nous allons essayer de créer une valeur limite du nombre de produits à retourner quand on fait appel à l'URI /Produits (méthode listeDesProduits
).
Rendez-vous dans application.properties de Microservice-produits, et ajoutez ceci :
spring.application.name=microservice-produits
server.port=9001
#Configurations H2
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=none
#Nos configurations
mes-configs.limitDeProduits= 4
Nous avons donc ajouté m es-configs.limitDeProduits= 4
. Il faut veiller à ce que tous les noms de vos propriétés soient précédés d'un préfixe afin qu'elles soient identifiables plus tard. Dans notre cas, le préfixe est mes-configs.
Afin de récupérer les valeurs que nous avons indiquées dans application.configuration, nous allons utiliser l'annotation @ConfigurationProperties
. Pour ce faire, nous allons créer une classe de configuration.
Créez une classe appelée ApplicationPropertiesConfiguration dans un package nommé configurations.
ApplicationPropertiesConfiguration.java
package com.mproduits.configurations;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("mes-configs")
public class ApplicationPropertiesConfiguration
{
private int limitDeProduits;
public int getLimitDeProduits()
{
return limitDeProduits;
}
public void setLimitDeProduits(int limitDeProduits)
{
this.limitDeProduits = limitDeProduits;
}
}
Explications :
@Component
: demande à Spring de scanner cette classe à la recherche de configurations.@ConfigurationProperties("mes-configs")
: précise que cette classe de configuration va récupérer des propriétés dans application.properties dont le préfixe est mes-configs.Il suffit ensuite de déclarer des propriétés avec les mêmes noms que celles du fichier de configuration. Dans notre cas, il s'agit de
limitDeProduits
. Il ne faut pas oublier de générer les Getters et Setters.
Il nous suffit à présent de retourner dans notre contrôleur pour accéder aux valeurs très simplement.
Extrait de ProductController.java :
private final ProductDao productDao;
private final ApplicationPropertiesConfiguration appProperties;
public ProductController(ProductDao productDao, ApplicationPropertiesConfiguration appProperties){
this.productDao = productDao;
this.appProperties = appProperties;
}
// Affiche la liste de tous les produits disponibles
@GetMapping(value = "/Produits")
public List<Product> listeDesProduits()
{
List<Product> products = productDao.findAll();
if(products.isEmpty()) throw new ProductNotFoundException("Aucun produit n'est disponible à la vente");
List<Product> listeLimitee = products.subList(0, appProperties.getLimitDeProduits());
return listeLimitee;
}
Explications :
appProperties.getLimitDeProduits()
va retourner le chiffre 4 que nous avons défini dans le fichier de configuration. On le passe donc àsubList
qui coupe une liste donnée à la limite donnée en 2e argument.
Voilà, le tour est joué ! Vous pouvez définir autant de valeurs que vous le souhaitez dans le fichier de configuration, et y accéder très simplement dans votre code.
Il ne vous reste qu’à relancer vos services puis vous rendre sur http://localhost:8080/Accueil, pour voir les 4 premiers produits.
Découvrez comment fonctionne l'externalisation
Afin d'externaliser les fichiers de configuration, nous allons utiliser un Edge Microservice appelé Spring Cloud Config. Il se positionne comme serveur de distribution des fichiers de configuration.
Il suffit de placer tous les fichiers de configuration dans un dépôt GIT. Grâce à des conventions de nommage de fichiers, Spring Cloud Config sera capable de savoir quel fichier va servir à quel microservice, en se basant sur le nom du microservice.
Pour modifier plus tard la configuration d'un microservice, il suffira de pousser les changements dans le GIT. Spring Cloud Config se mettra alors à servir la nouvelle version ! Pas besoin d'arrêter le moindre microservice !
Créez le dépôt GIT
Commençons par le bas du diagramme, et créons un dépôt GIT dans lequel iront nos fichiers de configuration.
Créez un compte sur GitHub (si vous n'en avez pas déjà un), puis créez un dépôt et appelez-le par exemple mcommerce-config-repo.
Revenez dans le dossier du projet Mcommerce sur votre ordinateur, et créez un nouveau dossier appelé config-server-repo. Nous allons mettre dans ce dossier les fichiers de configuration des microservices, puis nous le connecterons au dépôt distant que vous avez créé.
Importez ce dossier en tant que nouveau module, comme vous l'avez fait pour les autres microservices. La différence est que, cette fois, vous choisirez "Create module from existing source" :
Continuez l'importation par défaut. Il n'y a pas besoin de choisir de dossier "source" ou "resources".
Le dossier apparaîtra alors à gauche dans votre projet à côté des autres microservices.
Créez maintenant un nouveau fichier dans ce dossier :
Appelez-le microservice-produit.properties.
spring.application.name=microservice-produits
C'est grâce à cette correspondance de noms que notre serveur de configuration fera le lien entre ce fichier et le microservice correspondant.
Coupez le contenu d'application.properties du Microservice-produits, excepté le nom de celui-ci, et collez le tout dans le nouveau fichier créé :
microservice-produits.properties
server.port=9001
#Configurations H2
spring.jpa.show-sql=true
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=none
#Nos configurations
mes-configs.limitDeProduits= 4
Dans application.properties du Microservice-produits, il vous restera donc :
spring.application.name=microservice-produits
Rendez-vous au terminal d'IntelliJ :
Pointez vers le dossier créé :
cd config-server-repo/
Initialisez un nouveau dépôt GIT local que nous allons pousser plus tard vers le distant :
git init
Ajoutez les fichiers du dossier au GIT :
git add .
Ajoutez l'URL du dépôt distant à celui-ci :
git remote add origin https://NOM-UTILISATEUR:MOT-DE-PASSE@github.com/AmarMicroDev/mcommerce-config-repo.git
Veillez à faire les remplacements de nom d'utilisateur et de mot de passe, ainsi qu'à mettre votre propre URL à la place de la mienne.
Faites un commit du contenu :
git commit -m "Premier commit"
Poussez enfin le tout vers le dépôt distant :
git push -u origin master
Vous avez maintenant toutes les configurations du Microservice-produits disponibles dans le dépôt distant. Maintenant, mettons en place le serveur de configuration.
Utilisez Spring Cloud Config
Nous allons utiliser un starter afin de créer notre serveur de configuration.
Rendez-vous dans Spring Initializr, choisissez Spring Cloud Config, puis renseignez les champs comme suit :
Téléchargez et extrayez le dossier dans celui de notre projet. Importez-le ensuite comme nouveau module, exactement comme vous l'avez fait pour les autres microservices dans la première partie de ce cours.
Vous obtenez alors cette arborescence :
Pour que notre serveur fonctionne, nous avons besoin de lui indiquer où aller chercher les fichiers de configuration pour les servir ensuite à nos microservices.
Modifiez le pom pour qu’il ressemble à :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mcommerce</groupId>
<artifactId>config-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>config-server</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<spring-cloud.version>2020.0.4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
Rendez-vous donc à application.properties et renseignez l'URL de dépôt distant :
spring.cloud.compatibility-verifier.enabled=false
spring.application.name=config-server
server.port:9101
spring.cloud.config.server.git.uri=https://github.com/aiwanesk/config-server-repo.git
Tous nos Edge Microservices seront sur des ports commençant par 91. Celui de config-server est donc 9101. N'oubliez pas de renseigner également son nom (config-server).
Il suffit maintenant de déclarer ce microservice comme étant un serveur de configuration, grâce à @EnableConfigServer
.
ConfigServerApplication
package com.mcommerce.configserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
Démarrez votre serveur Spring Cloud Config et rendez-vous à l'URL http://localhost:9101/microservice-produits/default.
Vous obtenez alors :
{
"name": "microservice-produits",
"profiles":
[
"default"
],
"label": null,
"version": "6c360ea596e6d23a71eaa53788643c40ac22acdd",
"state": null,
"propertySources":
[
{
"name": "https://github.com/aiwanesk/config-server-repo.git
/microservice-produits.properties",
"source":
{
"server.port": "9001",
"spring.jpa.show-sql": "true",
"spring.h2.console.enabled": "true",
"spring.datasource.sql-script-encoding": "UTF-8",
"mes-configs.limitDeProduits": "4"
}
}
]
}
Le serveur est donc allé chercher le fichier de configuration dans le GIT, et expose une API qui répond à l'URL "/nom-du-microservice/default". Il fournit ensuite sous format JSON toutes les configurations présentes dans le fichier.
Liez un microservice à Spring Cloud Config
Nous avons un dépôt distant relié à notre serveur Spring Cloud Config. Il reste à demander au Microservice-produits de récupérer le contenu de ces fichiers de configuration depuis le serveur de configuration.
Pour ce faire, il suffit de modifier pom.xml de Microservice-produits afin d'ajouter le starter spring-cloud-starter-config :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mproduits</groupId>
<artifactId>mproduits</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mproduits</name>
<description>Microservice de gestion des produits</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<spring-cloud.version>2020.0.4</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
Renommez ensuite application.properties en bootstrap.properties.
Puis ajoutez :
spring.cloud.config.uri=http://localhost:9101
Lancez config-server et Microservice-produits. Vous verrez dans la console en première ligne :
Fetching config from server at: http://localhost:9101
Cette ligne indique que la première chose que fait votre microservice est de récupérer la configuration via server-config.
Testez votre microservice "produits" en récupérant la liste des produits. Vous devriez n'en recevoir que 4, comme configuré dans le dépôt GitHub.
Actualisez la configuration de votre microservice
Nous avons un dernier problème à résoudre. Si vous changez la valeur de mes-configs.limitDeProduits
et que vous réinvoquez /Produits, vous remarquerez que rien ne change.
En effet, la configuration est chargée une seule fois au démarrage du microservice, pour des raisons évidentes de performance.
Vous avez néanmoins la possibilité de demander à votre microservice de s'actualiser et de recharger le fichier de configuration.
Pour ce faire, il faut lui envoyer un signal de "Refresh". Pour cela, nous devons ajouter Spring Actuator à notre microservice, qui nous exposera un URI /refresh permettant de forcer la réactualisation des valeurs de configuration.
Ajoutez donc Spring Actuator dans le pom.xml de Microservice-produits :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Actualisez Maven.
Rendez-vous dans bootstrap.properties (ou dans le fichier correspondant dans le GIT), et ajoutez ceci :
management.endpoints.web.exposure.include=refresh
Cela aura pour effet d'exposer l’endpoint refresh d'Actuator. Nous y reviendrons dans un prochain chapitre.
Nous allons maintenant indiquer à un de nos beans qui accède aux propriétés, de se rafraîchir à chaque fois qu'un événement Refresh est lancé. Nous allons donc ajouter l'annotation @RefreshScope
à ApplicationPropertiesConfiguration.java :
@Component
@ConfigurationProperties("mes-configs")
@RefreshScope
public class ApplicationPropertiesConfiguration
{
private int limitDeProduits;
public int getLimitDeProduits()
{
return limitDeProduits;
}
public void setLimitDeProduits(int limitDeProduits)
{
this.limitDeProduits = limitDeProduits;
}
}
Lancez tous les microservices, puis appelez Microservice-produits : localhost:9001/Produits.
Changez la valeur de mes-configs.limitDeProduits
dans le GIT, puis appelez de nouveau localhost:9001/Produits. Comme vous pouvez le constater, vous avez le même nombre de produits retournés. Cela indique que votre microservice n'a pas pris en compte ce changement.
Déclenchez alors un événement Refresh en envoyant une requête POST à http://localhost:9001/actuator/refresh via Postman. Vous obtenez alors ce résultat :
[
"config.client.version",
"mes-configs.limitDeProduits"
]
Ce retour vous indique clairement que mes-configs.limitDeProduits
a changé. Cela déclenche la réactualisation dans tous les beans annotés par @RefreshScope.
Maintenant, si vous retentez localhost:9001/Produits, vous avez le bon nombre de produits en retour.
Le bean ApplicationPropertiesConfiguration s'est actualisé et a récupéré les nouvelles valeurs mises à jour dans le GIT. Comme on accède à ces propriétés dans notre microservice exclusivement via ce bean, cette valeur est mise à jour partout.
Challenge : Essayez d'externaliser tous les fichiers de configuration des autres microservices !
En résumé
application.properties
peut être utilisé pour stocker des constantes auxquelles on peut accéder grâce à un bean annoté avec @ConfigurationProperties .Config-Server permet de récupérer les fichiers de configuration dans un dépôt, et de les servir aux microservices en se basant sur leurs noms.
Pour récupérer automatiquement ces configurations, il suffit d’ajouter le starter spring-cloud-starter-config, et de renommer application.properties en bootstrap.properties.
Pour actualiser à la volée la configuration d’un microservice en l’obligeant à récupérer une version fraîche du fichier .properties, il suffit d’envoyer un POST vers l'endpoint /refresh exposé par Spring Actuator.
Dans le prochain chapitre, vous allez apprendre à utiliser Eureka pour rendre vos mocroservices "découvrables". Voyons tout de suite ce que cela veut dire !