Vous êtes sur la page 1sur 77

Premier Micro-service avec

le FrameWork SpringBoot

1
L’architecture micro-services a été inventée pour résoudre certaines des difficultés causées par les gros projets.
Avec le temps, les projets informatiques ont tendance à grossir : on étend petit à petit les fonctionnalités existantes, on en ajoute d’autres, et
on supprime rarement les anciennes.

En même temps que le code et le projet s’étendent, un certain nombre de douleurs apparaissent :
Complexité
Quand la quantité de code augmente, le code devient de plus en plus complexe. Même avec une architecture logicielle solide, les
interdépendances entre les différentes briques augmentent avec le temps.

Cette complexité a deux désavantages :

Évolutivité et fiabilité
• Plus le temps passe, plus les nouvelles fonctionnalités métier deviennent complexes, et plus les différentes briques ont d’interactions. On
a beau organiser le code en couches et en composants, il y a toujours des cas particuliers et des rustines qui rendent les choses plus
floues.
• Au-delà d’un certain seuil, il devient impossible d’avoir en tête un modèle global du projet.
• Même avec une base de tests solide, la multiplication des effets de bord de chaque action rend le système moins fiable, et il devient alors
plus difficile d’ajouter proprement des nouvelles fonctionnalités et d’effectuer des « refactorings ».

« Scalabilité » horizontale
• Améliorer la « scalabilité » d’un système peut demander de modifier des éléments structurants du projet.
• Plus un projet est gros, plus ces interventions deviennent coûteuses et risquées.
• Le risque est alors de se retrouver avec un système qu’il est impossible de faire évoluer pour un nouveau cas d’usage.
2
Innovation
Innovation technologique
• Pour capitaliser les investissements et faciliter la gestion des personnes, il est normal de vouloir avoir une cohérence
entre les différents projets d’une entreprise : même manière de travailler, mêmes langages de programmation, mêmes
outils.
• Chaque projet est invité à suivre des choix transverses, et peut s’en écarter en fonction de ses spécificités, à condition de
le justifier.
• Pour les gros projets, la même tension a lieu à l’intérieur même des projets : pour éviter la fragmentation, chaque
évolution technique doit pouvoir être propagée à l’intégralité du code.
• Avec le temps, les modifications deviennent donc plus coûteuses, et il est plus difficile d’introduire de nouveaux outils
pour des besoins spécifiques.

Innovation métier
• Pour répondre aux nouveaux besoins métier, il faut être capable de ménager une zone d’innovation à l’intérieur des
projets.
• Car si certaines nouveautés sont mises en œuvre par de nouveaux projets, la plupart se font sur des projets existants.
• Or plus un projet est gros, plus il est critique pour l’entreprise, moins on va prendre de risques de le modifier pour tester
de nouveaux produits ou de nouveaux marchés, et petit à petit les enjeux de stabilité vont prendre le pas sur la capacité
d’innovation.

3
« Le découpage se fait par domaine métier, en groupant les
services et les types de données qui ont des liens forts, et en
séparant quand ils sont suffisamment indépendants. »

4
Les avantages de l’approche micro-services
Complexité
Évolutivité et fiabilité

Contraindre la taille limite les cas particuliers, et permet d’avoir en tête l’intégralité des comportements.

La dette technique est gardée sous contrôle, et le code est ainsi capable d’évoluer. Passer par des appels de services pour communiquer avec
les autres domaines formalise les échanges.
Les contrats d’interface sont alors plus carrés, et il est plus facile de prendre en compte tous les cas, y compris les cas d’erreurs.

Scalabilité horizontale

Avec des applications d’une taille limitée, il est plus facile d’augmenter la « scalabilité » en « refactorant » le code ou en la réécrivant
complètement en fonction des nouveaux besoins.

Innovation

Innovation technologique

Les bases de codes et les équipes sont indépendantes et peuvent donc faire leurs choix techniques en fonction de leurs besoins propres.

Innovation métier

Si tout le système d’information est structuré en services, il est facile d’expérimenter en démarrant un nouveau projet s’appuyant sur 5les
données des autres, et plus facile de décomissionner car c’est l’ensemble d’un projet qui sera supprimé.
Parler Micro-Services c’est bien, en faire c’est mieux
Les Micro-Services ont cette fâcheuse tendance à faire parler les gens plutôt qu'à les faire coder.

Il partage d’ailleurs ce biais avec son lointain ancêtre/collègue/SOA.

Il faut dire qu'à l'époque SOA (il y a 5-10 ans donc), il fallait faire avec SOAP, les ESB (EAI « rebrandés »); il valait
mieux en parler qu’en faire.

Mais maintenant tout n’a pas changé mais nous avons enfin les technologies nous permettant de mettre en place
l’ensemble des architectures/solutions rêvées il y a quelques années (Relisez :
http://shop.oreilly.com/product/9780596006754.do ), le livre a plus de 10 ans, si c’est pas du MicroServices, ça y
ressemble.

Plus d’excuse pour ne plus en faire :


Spring Boot est un très bon point d’entrée pour mettre en œuvre
une architecture MicroServices : simplicité, légèreté …

6
L’approche Micro-Services avec SpringBoot

7
Spring Boot est un projet ou un micro framework qui a notamment pour but de faciliter la configuration
d’un projet Spring et de réduire le temps alloué au démarrage d’un projet.

Pour arriver à remplir cet objectif, Spring Boot se base sur plusieurs éléments :

•Une génération rapide de la structure de votre projet en y incluant toutes les dépendances Maven
nécessaires à votre application.

•L’utilisation de « Starters » pour gérer les dépendances.


Spring a regroupé les dépendances Maven de Spring dans des « méga dépendances » afin de faciliter la
gestion de celles-ci.

Par exemple si vous voulez ajouter toutes les dépendances pour gérer la sécurité il suffit d’ajouter le starter « spring-boot-
starter-security ».

8
•L’auto-configuration, qui applique une configuration par défaut au démarrage de votre application
pour toutes dépendances présentes dans celle-ci.

•Cette configuration s’active à partir du moment où vous avez annoté votre application avec
« @EnableAutoConfiguration » ou « @SpringBootApplication ».

•Bien entendu cette configuration peut-être surchargée via des propriétés Spring prédéfinie ou via une
configuration Java.

•L’auto-configuration simplifie la configuration sans pour autant vous restreindre dans les
fonctionnalités de Spring.
Par exemple, si vous utilisez le starter « spring-boot-starter-security », Spring Boot vous configurera la sécurité dans votre
application avec notamment un utilisateur par défaut et un mot de passe généré aléatoirement au démarrage de votre
application.

9
• En plus de ces premiers éléments qui facilitent la configuration d’un projet, Spring Boot offre d’autres
avantages notamment en termes de déploiement applicatif.

• Habituellement, le déploiement d’une application JEE nécessite la génération d’un fichier .war qui doit
être déployé sur un serveur comme un Apache Tomcat.
Spring Boot simplifie ce mécanisme en offrant la possibilité d’intégrer directement un serveur Tomcat
dans votre exécutable. Au lancement de celui-ci, un Tomcat embarqué sera démarré afin de faire
fonctionner votre application.

• Enfin, Spring Boot met à disposition des opérationnels, des métriques qu’ils peuvent suivre une fois
l’application déployée en production. Pour cela Spring Boot utilise « Actuator » qui est un système qui
permet de monitorer une application via des URLs spécifiques ou des commandes disponibles via SSH
« CraSH ». Vous avez toujours la possibilité de monitorer également vos services avec JMX.

10
• Accélérer le développement d’applications JEE

• Dépendances prêtes à l’emploi

• Auto-Configuration

• StandAlone application

• Déploiement simplifié

• Tomcat/Jetty en mode embarqué.

• Fonctionnalités pour la production.

• Pas de génération de code/de XML

11
Technologies supportées

Core : Spring Security, JTA, Spring Cache, Spring Session


Web : Spring MVC, WebSocket, Jersey, Mobile, HATEOS
Moteur de templates : Freemaker, Thymeleaf, Groovy, Mustache
Database :
SGBDR : Spring Data JPA, JDBC, JOOQ, Hibernate
NoSql : Redis, MongoDB, ElasticSearch, Cassandra
Spring Cloud : Eureka, Hystrix, Turbine, AWS, OAuth2
I/O : Spring Batch et Integration, JavaMail, Camel, JMS, AMQP
Social : Facebook, Linkedln, Twitter

12
13
Prérequis indispensable
Installer le jdk 8 dans sa dernière version
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

Penser à définir la variable JAVA_HOME correctement,


Ainsi que votre PATH

echo %JAVA_HOME%
echo %PATH%
14
Prérequis +/- indispensable
Installer NetBeans 8.2 pour JEE https://netbeans.org/downloads/
Essentiellement pour la prise en charge du HTML et JavaScript

15
Prérequis +/- indispensable
Installer le plugin NetBeans pour SpringBoot Ce plugins propose la création d’un projet Maven Spring Boot simplifié)
NB Spring Boot version 1.5

16
Prérequis indispensable
installer Maven version 3.5.0

https://maven.apache.org/download.cgi

Ajouter le chemin
C:\apache-maven-3.5.0-bin\apache-maven-3.5.0\bin
Dans la variable PATH
Tester l’installation et la version

17
Backend MicroService via WS REST et AMQP
3
Microservice
Avec persistence
Des données

Front WS-REST Microservice WS-REST Microservice


Browser MiddleWare Backend
Internet 1 Dispatcher 2 REST Service

Message amqp

Bus AMQP RabbitMQ

Message amqp

Service Backend
AMQP
1 Consommateur

18
Créer un premier package Maven Basic SpringBoot 1

Sous NetBeans :
File | New Project | Maven | Spring Boot basic project

19
1 Refactorisation du projet :
Nom du projet(1) , chemin du package(2), nom du fichier principal Java (3)
(Clic droit + renommer ou clic droit + Refactor + Renommer)

Re-factoriser le package de test également


Chemin du package de test (4) , nom du fichier principal Java de test (5)

20
Créer un premier package Maven SpringBoot Initializr
Sous NetBeans :
File | New Project | Maven | Spring Boot Initializr project

21
Vous pouvez également créer votre projet via le site Spring InitialiZr

https://start.spring.io/

22
1
Ajouter les dépendances complémentaires au projet (spring-boot-starter-web)
Clic droit sur « dependencies » de notre projet

Ajouter la dépendance spring-boot-starter-web

23
1
Ajouter la dépendance amqp RabbitMQ client amqp-client

24
1 Accéder au fichier pom.xml sous NetBeans
Vous accédez au fichier pom via le menu contextuel sur la zone de projet NetBeans, menu « Open POM »

Ce qui vous permet d’accéder en écriture sur le fichier POM et de


corriger les informations de votre projet comme ci-dessous par
exemple

<groupId>com.ht.dev</groupId>

<artifactId>PremierMicroService</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>

<name>PremierMicroService</name>
<description>Basic project for Spring
Boot</description>

25
Exemple de code source qui permet de mettre en place 3 web services REST et un Producteur pour rabbitMQ. 1
package com.ht.dev;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

//@SpringBootApplication
@Controller
@EnableAutoConfiguration
@SpringBootConfiguration
public class PremierMicroService {

public static void main(String[] args) {


SpringApplication.run(PremierMicroService.class, args);
} 26
@RequestMapping("/hello") 1
@ResponseBody
String home() {
return "Salut les licences 3 alt!";
}

@RequestMapping("/message")
@ResponseBody
String message() throws TimeoutException {
String QUEUE_NAME = "hello";
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
String message = "Salut les licences 3 alt!";
try
{
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
channel.close();
}
catch (IOException ex) {
return ex.getMessage();
}
return " [x] Envoyé '" + message + "'";
}
27
@RequestMapping(value="/message/{msg}", method=RequestMethod.GET) 1
@ResponseBody
String messageMsg(@PathVariable("msg") String msg) throws TimeoutException {
String QUEUE_NAME = "hello";
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
try
{
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();

channel.queueDeclare(QUEUE_NAME, false, false, false, null);


channel.basicPublish("", QUEUE_NAME, null, msg.getBytes("UTF-8"));
System.out.println();

channel.close();
}
catch (IOException ex) {
return ex.getMessage();
}
return " [x] Envoyé '" + msg + "'";
}

} //fin de programme

28
désactiver "Compile on Save" avant de compiler et exécuter le projet sous NetBeans
Clic droit sur le projet, puis menu « Properties »

29
cmd /c "\"\"C:\\Program Files\\NetBeans 8.2\\java\\maven\\bin\\mvn.bat\" -Drun.jvmArguments=\"-noverify -XX:TieredStopAtLevel=1 -Xms64m\" -
Drun.mainClass=com.example.BasicApplication -Dmaven.ext.class.path=\"C:\\Program Files\\NetBeans 8.2\\java\\maven-nblib\\netbeans-eventspy.jar\" -Dfile.encoding=UTF-8
spring-boot:run\""
Scanning for projects...

Tester notre MicroService ------------------------------------------------------------------------


Building PremierMicroService 0.0.1-SNAPSHOT
------------------------------------------------------------------------

Lancer le projet sous NetBeans >>> spring-boot-maven-plugin:1.5.2.RELEASE:run (default-cli) @ basic >>>

--- maven-resources-plugin:2.6:resources (default-resources) @ basic ---


Using 'UTF-8' encoding to copy filtered resources.
Copying 1 resource
Copying 0 resource

--- maven-compiler-plugin:3.1:compile (default-compile) @ basic ---


Nothing to compile - all classes are up to date

--- maven-resources-plugin:2.6:testResources (default-testResources) @ basic ---


Using 'UTF-8' encoding to copy filtered resources.
skip non existing resourceDirectory C:\TEMP\PremierMicroservice\src\test\resources

--- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ basic ---


Nothing to compile - all classes are up to date

<<< spring-boot-maven-plugin:1.5.2.RELEASE:run (default-cli) @ basic <<<

--- spring-boot-maven-plugin:1.5.2.RELEASE:run (default-cli) @ basic ---


Attaching agents: []

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.2.RELEASE)

2017-05-07 00:08:52.493 INFO 8764 --- [ main] com.ht.dev.PremierMicroService : Starting PremierMicroService on PC104160 with PID 8764
(C:\TEMP\PremierMicroservice\target\classes started by tondeur-h in C:\TEMP\PremierMicroservice)
2017-05-07 00:08:52.493 INFO 8764 --- [ main] com.ht.dev.PremierMicroService : No active profile set, falling back to default profiles: default
2017-05-07 00:08:52.540 INFO 8764 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing
org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@13eb8acf: startup date [Sun May 07 00:08:52 CEST 2017]; root of context hierarchy
2017-05-07 00:08:53.601 INFO 8764 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2017-05-07 00:08:53.601 INFO 8764 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2017-05-07 00:08:53.601 INFO 8764 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.11
2017-05-07 00:08:53.679 INFO 8764 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2017-05-07 00:08:53.679 INFO 8764 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1155 ms
2017-05-07 00:08:53.804 INFO 8764 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
2017-05-07 00:08:53.804 INFO 8764 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-05-07 00:08:53.804 INFO 8764 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-05-07 00:08:53.804 INFO 8764 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-05-07 00:08:53.804 INFO 8764 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2017-05-07 00:08:54.006 INFO 8764 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice:
org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@13eb8acf: startup date [Sun May 07 00:08:52 CEST 2017]; root of context hierarchy
2017-05-07 00:08:54.038 INFO 8764 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/message]}" onto java.lang.String
com.ht.dev.PremierMicroService.message() throws java.util.concurrent.TimeoutException
2017-05-07 00:08:54.038 INFO 8764 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/hello]}" onto java.lang.String
com.ht.dev.PremierMicroService.home()
2017-05-07 00:08:54.038 INFO 8764 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public
org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>>
org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2017-05-07 00:08:54.038 INFO 8764 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public
org.springframework.web.servlet.ModelAndView
org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-05-07 00:08:54.069 INFO 8764 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class
org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 30
2017-05-07 00:08:54.069 INFO 8764 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class
org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-05-07 00:08:54.100 INFO 8764 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class
Tester les web service avec un browser
http://localhost:8080/hello
http://localhost:8080/message
http://localhost:8080/message/Bonjour le monde du microservice

31
Compiler et packager avec Maven

Se rendre dans le dossier du projet ou se trouve le fichier pom.xml et lancer la commande Maven :
mvn package
Se rendre ensuite dans le dossier Target ou a été généré notre package Jar

32
Lancer le microService avec la commande Maven
mvn spring-boot:run
OU JAVA
java –jar basic-0.0.1-SNAPSHOT.jar

33
Exécution du consommateur

34
Backend MicroService via WS REST et AMQP
3
Microservice
Avec persistence
Des données

Front WS-REST Microservice WS-REST Microservice


Browser MiddleWare Backend
Internet 1 Dispatcher 2 REST Service

Message amqp

Bus AMQP RabbitMQ

Message amqp

Service Backend
AMQP
1 Consommateur

35
1 Ajouter un appel supplémentaire de Web service (au service Backend) à notre projet PremierMicroService
public class PremierMicroService {

private RestTemplate template=new RestTemplate();

//mapper dans le fichier application.properties


@Value("${backend.backendServiceHost}")
String backendServiceHost;
@Value("${backend.backendServicePort}")
int backendServicePort;

@RequestMapping(value="/backend", method=RequestMethod.GET, produces="text/plain")


@ResponseBody
String CallBackend() {
//construire et appeler le service Backend
String backendServiceURL=String.format("http://%s:%d/backend",
backendServiceHost, backendServicePort);

BackendDTO reponse=template.getForObject(backendServiceURL, BackendDTO.class);

//return backendServiceURL;
return reponse.getReponse();
}

36
1 Classe POJO BackEndDTO Placer les paramètre du WS dans le fichier
Application.properties

package com.ht.dev; management.security.enabled=false


backend.backendServiceHost=localhost
public class BackEndDTO { backend.backendServicePort=8081
String reponse;

public String getReponse() {


return reponse;
}

public void setReponse(String reponse) {


this.reponse = reponse;
}

37
2 Création du service Backend BackendMicroService

package com.ht.dev;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@SpringBootApplication
@Controller
public class BackendMicroService {

@RequestMapping(value="/backend", method=RequestMethod.GET, produces="application/json")


@ResponseBody
public Reponse backendReponse(){
return new Reponse(String.format("Bonjour de la part du Backend"));
}

public static void main(String[] args) {


SpringApplication.run(BackendMicroService.class, args);
}
} 38
2 La classe Reponse.java (Un pojo d’encapsulation permettant de générer la réponse en Json(par défaut))

package com.ht.dev;

class Reponse{
String reponse;

public String getReponse() {


return reponse;
}

public Reponse(String format) {


reponse=format;
}
}

39
2
Avant de lancer le microService BackendMicroService, ne pas oublier de spécifier le port 8081 pour le
démarrage de ce service.
On va spécifier cette valeur de port dans le fichier application.properties

server.port=8081

40
Monitorer vos microServices avec JMX
Par défaut Spring Boot active les objets JMX dans son code, ce qui permet de monitorer vos microservices avec
l’outils « Java Mission Control » jmc.exe qui se trouve dans le bin du JDK.

41
Monitorer vos microServices avec Spring Boot Starter Actuator 1.5.3
Insérer la dépendance suivant dans votre projet, ceci permet d’ajouter des Web Services à votre application
<dependency> Placer dans le fichier de config application.properties
<groupId>org.springframework.boot</groupId> management.security.enabled=false
<artifactId>spring-boot-starter-actuator</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
Example : le EndPoint http://localhost:8080/env ressemblera à :

• http://localhost:8080/beans
• http://localhost:8080/env
• http://localhost:8080/health
• http://localhost:8080/metrics
• http://localhost:8080/trace
• http://localhost:8080/mappings
42
Codecentric a développé une UI intéressante que l’on peut cloner à partir de GitHub.

https://github.com/codecentric/spring-boot-admin

43
Backend MicroService via WS REST et AMQP
3
Microservice
Avec persistence
Des données

Front WS-REST Microservice WS-REST Microservice


Browser MiddleWare Backend
Internet 1 Dispatcher 2 REST Service

Message amqp

Bus AMQP RabbitMQ

Message amqp

Service Backend
AMQP
1 Consommateur

44
package com.ht.dev; Couche Persistance avec JDBCTemplate
3
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Créer un nouveau projet et ajouter les
import org.springframework.beans.factory.annotation.Autowired; Dépendance ci-dessous
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@SpringBootApplication
@Controller
public class BackendJDBCMicroService implements CommandLineRunner{

private static final Logger log = LoggerFactory.getLogger(BackendJDBCMicroService.class);

@Autowired
JdbcTemplate jdbcTemplate;

@RequestMapping("/name/{NOM}/fname/{PRENOM}")
@ResponseBody
public String Insert_Person(@PathVariable("NOM") String nom, @PathVariable("PRENOM") String prenom){
jdbcTemplate.update("INSERT INTO APP.persons(first_name, last_name) VALUES (?,?)", nom, prenom);
return "Insertion "+nom+" "+prenom+" ok";
} 45
3

@Override
public void run(String... strings) {

log.info("Creation de la table");

try {
//jdbcTemplate.execute("DROP TABLE persons");
jdbcTemplate.execute("CREATE TABLE APP.persons(id INTEGER, first_name VARCHAR(255), last_name VARCHAR(255))");
}
catch(DataAccessException e) {}
}

public static void main(String[] args) {


SpringApplication.run(BackendJDBCMicroService.class, args);
}
}

46
3 Classe qui va servir de rowMapper lors des Query

package com.ht.dev;

public class Person {

private long id;


private String firstName, lastName;

public Person(long id, String firstName, String lastName) {


this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}

@Override
public String toString() {
return String.format(
"Person[id=%d, firstName='%s', lastName='%s']",id, firstName,
lastName);
}

47
Avant de lancer le microService BackendJDBCMicroService, ne pas oublier de spécifier les données d’accés à la
base de données Java Derby embedded.
On va spécifier ces valeurs dans le fichier application.properties

spring.datasource.driver-class-name=org.apache.derby.jdbc.EmbeddedDriver
spring.datasource.url=jdbc:derby:backendJDBCMicroService;create=true
spring.datasource.username=bjms
spring.datasource.password=bjms

48
3 Requêter dans la base de données

List<Person> persons = new ArrayList<Person>();

List<Map<String,Object>> rows=jdbcTemplate.queryForList("SELECT * FROM APP.persons WHERE


last_name='TONDEUR'");

for (Map rs : rows) {


Person personne = new Person((Long)rs.get("id"), (String)rs.get("first_name"),
(String)rs.get("last_name"));
persons.add(personne);
}

49
Exemple d’une réponse HTTP et d’un corps au format JSON

@RequestMapping(value="/arnum/{cle}/IPP={IPP}",method = RequestMethod.GET)
private ResponseEntity<List<Patient>> getByName(@PathVariable("cle") String cle,@PathVariable(« IPP") String
IPP)
{

List<Patient> patients=new ArrayList<>();

List<Map<String,Object>> rows =jdbcTemplate.queryForList(sql);


rows.forEach((row) -> {
Patient p=new Patient();
p.setIPP("12345");

patients.add(p);
});

//si la liste est vide


if (patients.isEmpty()) {
return new ResponseEntity(HttpStatus.NO_CONTENT); //pour être RESTfull}
//sinon on retourne la liste
return new ResponseEntity<>(patients, HttpStatus.OK); //et le status 200 OK
}

50
Notions MVC avec Spring Boot

Toutes les pages statiques doivent se trouver dans le dossier ./static


Il est nécessaire d’inserer une dépendance Web et ThymeLeaf pour réaliser l’appel d’un Template de vue.
Les Templates de vue seront développé sous ThymeLeaf de préférence.
On place les Template dans le dossier ./template du projet.

Chaque projet développé en MVC, doit principalement présenter une classe Controller (dispatcher), une ou des
classes métiers pour les accès aux ressources, et des Template écrit en HTML et ThymeLeaf.

On utilisera la classe Model qui permettra de faire la liaison entre notre controller et les Template.

51
package com.ht.dev.testms;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan; Déclaration d’une application SpringBoot
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
Dispatcher de l’appel de la
@SpringBootApplication page Web ./bonjour en type
@Controller Déclaration de la classe controller POST
@ComponentScan
public class TestMsApplication {

@RequestMapping (value="/bonjour",method=RequestMethod.POST)
private String bonjour(@RequestParam(name="nom", required=false, defaultValue="Hervé") String nom,
Model model)
{
model.addAttribute("nom", nom); Utilisation de la classe Model pour l’appel
return "bonjour";
du Template HTML « bonjour.html » Mapping de la variable
}
nom transmis par le
public static void main(String[] args) { protocole POST avec la
SpringApplication.run(TestMsApplication.class, args); variable locale nom
}
}
52
Exemple de Template ThymeLeaf (page bonjour.html)

Voir documentation ThymeLeaf :


http://www.thymeleaf.org/doc/tutorials/2.1/usingthymeleaf.html
http://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html

<!DOCTYPE html>

<html>
<head>
<title>Page Bonjour</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
</head>
<body>
<div>
<p th:text="'Bonjour '+${nom}+', bienvenue sur cette page'"/>
</div>
</body>
</html>
53
Discovery Service : Eureka
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.
Il sera nécessaire d’éviter d'appeler un service directement via sa localisation (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.
On mettra donc en place dans notre MS un service de Discovery.

On distinguera le "Server-Side Discovery" du "Client-Side Discovery".

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


https://www.todaysoftmag.com/article/1429/micro-service-discovery-using-netflix-eureka

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 plus simple.

54
55
Discovery Service Exemple
Serveur NetFlix
Eureka
discovery

1 1

Microservice Use WS-REST Microservice


FrontEnd Backend
Dispatcher 3 REST Service

3 (1) Nos services Backend Et FrontEnd vont s’enregistrer auprès du


Front service NetFlix Eureka discovery.
Browser (2) Notre service FrontEnd va interroger le service Discovery pour
Internet connaitre le Host et le port du service BackEnd lors de l’appel
WS (3) provenant du client via le service FrontEnd.
56
Service Discovery Registry (server side)
•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 le service Eureka Registry.

Créer un nouveau projet SpringInitializr et insérer la dépendance Maven « Eureka discovery » et « Eureka Server »
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>
57
La classe DemoApplication est générée et annotée avec @SpringBootApplication de Spring-Boot, nous
l'annotons également 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.

Fichier : application.properties du projet


server.port=8761
eureka.instance.hostname=localhost
eureka.client.fetchRegistry=false
eureka.client.registerWithEureka=false
logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
eureka.environment=prod
eureka.datacenter=HTMain

58
Tester le service Eureka
Il suffit d'exécuter le projet mvn clean package spring-boot:run puis de saisir dans le browser
l'url localhost:8761 pour voir s'afficher la page du service Eureka.

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

59
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.

•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".

60
Créer un nouveau projet SpringInitializr et insérer la dépendance Maven « Eureka discovery »

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

Annoter la classe générée


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

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.

61
Configurer le Client BackEnd Eureka

Fichier : application.properties du projet

spring.application.name=back
server.port=8081
eureka.client.fetchRegistry=true
eureka.client.registerWithEureka=true
eureka.client.useDnsForFetchingServiceUrls=false
instance.metadataMap.instanceId=${spring.application.name}:${server.port}

62
Controller Rest de l’application BackEnd
package com.dev.ht.clientbackdiscovery;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
@EnableEurekaClient

public class ClientbackdiscoveryApplication {

@RequestMapping({"/info","/"})
Mise à disposition d’un service
String info(){return ="Je suis la pages d'infos du back service";} info pour Eureka

@RequestMapping({"/bonjour"})
@ResponseBody
String bonjour(){return "Bonjour tout le monde!";}

public static void main(String[] args) {


SpringApplication.run(ClientbackdiscoveryApplication.class, args);
}
}

63
Configurer le Client FrontEnd Eureka

Fichier : application.properties du projet (autre paramètres possibles)

spring.application.name=front
server.port=8090
eureka.client.fetchRegistry=true
eureka.client.registerWithEureka=true
eureka.client.useDnsForFetchingServiceUrls=false
eureka.client.eurekaServerDNSName=localhost
eureka.client.eurekaServerPort=8761
eureka.client.eurekaServerURLContext=eureka
instance.metadataMap.instanceId=${spring.application.name}:${server.port}

64
Controller Rest de l’application FrontEnd qui pourra appeler les services BackEnd
package com.dev.ht.clientfrontdiscovery;
@SpringBootApplication
@RestController import java.util.List;
@EnableEurekaClient import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.beans.factory.annotation.Autowired;
public class ClientfrontdiscoveryApplication { import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Autowired import org.springframework.cloud.client.ServiceInstance;
private DiscoveryClient discoveryClient; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
public String serviceUrl() {
final StringBuilder sb=new StringBuilder();
List<ServiceInstance> instance = discoveryClient.getInstances("back");
instance.forEach(
si ->
{
sb.append ("Host= ");
sb.append(si.getHost());
sb.append(" Uri= ");
sb.append(si.getUri());
sb.append(" Port= ");
sb.append(si.getPort()); }
);
System.out.println("sb= "+sb.toString());

return "taille : "+instance.size();


}

@RequestMapping("/discover")
public String index() {return serviceUrl();}

public static void main(String[] args) {


SpringApplication.run(ClientfrontdiscoveryApplication.class, args);
} 65
}
Permet d’appeler le service REST /info de notre
application

66
Circuit breaker, un pattern pour fiabiliser vos microservices (ou systèmes distribués)

Les “Illusions de l’informatique distribuée” nous mène a se poser les questions :

Le réseau est fiable.


Le temps de latence est nul.
La bande passante est infinie.
Le réseau est sûr.
La topologie du réseau ne change pas.
Il y a un et un seul administrateur réseau.
Le coût de transport est nul.
Le réseau est homogène.
Et ces illusions ne prennent pas en compte la partie dépendance et leurs lots de problèmes (crash, temps de réponse lent,
réponse non conforme…).

Pour répondre à ces défis, la philosophie de « design for failure » (les traitements applicatifs doivent, dès leur conception,
prévoir le cas où les composants qu’ils appellent pourraient tomber en erreur) a pris encore plus d’importance.

67
Autres solutions
lift and shift Il suffit de prendre son application telle quelle et de la mettre sur le cloud sans faire de
modification (technique du lift and shift) => Utilisation de services en haute disponibilité & Utilisation
d’une infrastructure moderne => Cela n’est pas suffisant si l’application ne supporte pas la charge ou mal
développé (IP en fixe par exemple), le cloud peut couter cher également.

Utilisation de répartiteur de charge Utilisation de HAProxy par exemple (répartiteurs de


requêtes)=> Impossibilité d’ajout de répartiteur de
charge dans certains cas (service externe, protocole
non supporté, stockage des données) =>
Complexification de l’architecture et donc de
l’exploitation et du diagnostic des problèmes de
production => Complexité de trouver la bonne
configuration pour les lignes de vie (health check)

68
Design pattern : Timeout Il permet de ne pas attendre indéfiniment une
réponse en positionnant un temps d’attente
maximal=> L’erreur n’arrivera qu’après le temps
d’attente maximum (contraire au fail fast)=>
Consommation de ressource (connexion,
mémoire) inutile

Design pattern : Retry Pattern Il consiste à envoyer à nouveau la requête qui a


échoué. Et donc si le service appelé “tombe en
marche”, cela sera transparent pour l’utilisateur
au prix d’une latence significative=>Si le service
appelé reste hors service, un risque de surcharge de
l’application en multipliant les requêtes

69
Circuit Breaker

Le circuit breaker permet de contrôler la collaboration entre différents services afin d’offrir une grande tolérance à la latence
et à l’échec en fonction d’un certain nombre de critères d’erreur (timeout, nombre d’erreurs, élément dans la réponse), ce
pattern permet de désactiver l’envoi de requêtes au service appelé et de renvoyer plus rapidement une réponse alternative
de repli (fallback), aussi appelé graceful degradation.
Il agit comme un proxy implémentant une machine à états (Ouvert, Passant (fermé), Semi-ouvert) pour l’apprentissage de
l’état du service.

70
Lorsque le nombre d’échecs successifs dépasse un seuil, le circuit s’ouvre pour
ne plus laisser passer de requêtes. À ce moment-là, deux mécanismes se
déclenchent :

Mise en place de la réponse alternative de repli

Activation du processus du passage à l’état semi-ouvert (Déclenchement d'un minuteur).

Une fois le seuil de passage à l’état semi-ouvert atteint (seuil du temps d’attente), le circuit breaker laisse à nouveau passer
quelques requêtes et passe dans l’état passant si tout se déroule bien.

Nous pouvons envisager une infinité de possibilités :

Stocker toutes les requêtes en erreur avec le maximum de détail pour les traiter plus tard (AMQP / BDD)
Avoir plusieurs stratégies de réponse alternative en fonction du type d’erreur renvoyé par le service appelé (code “HTTP 503
Service Unavailable”, mauvaise réponse…)
Avoir des seuils intelligents qui s’adaptent après une période d’apprentissage
71
Graceful degradation

Adaptation automatique de l’application à une situation dégradée.

Exemple : mise en indisponibilité du service, ou affichage d’une réponse alternative.

But => Ne pas afficher de stackTrace à l’utilisateur.

72
Hystrix

https://github.com/Netflix/hystrix/wiki

Hystrix est une API open source développée par Netflix qui implémente le pattern circuit
breaker.

Le but d’Hystrix est de rendre une application résiliente aux pannes de ses dépendances
externes en arrêtant momentanément de les invoquer le temps de leur indisponibilité. On
dit alors que Hystrix ouvre le circuit.

Comme Hystrix n’est pas averti de la reprise d’une dépendance tombée, il tente, à
intervalles réguliers, d’appeler cette dépendance.

Dès que cette dernière est rétablie, il ferme alors son circuit et tous les appels qu’il reçoit
sont transmis à cette dépendance.
73
Hystrix constitue un point d’accès unique à une dépendance externe. Il englobe (wrappe) les appels clients dans un objet
suivant le pattern Command. Concrètement, chaque appel distant est wrappé dans un objet HystrixCommand.
Hystrix fonctionne avec deux modes, thread et sémaphore :

Thread : Hystrix maintient son propre pool de threads. Un appel distant est exécuté par un thread Hystrix. Une fois que
tous les threads sont pris, les nouveaux appels arrivants sont rejetés.

Sémaphore : l’appel distant est exécuté par le même thread du client appelant. Une fois que le nombre de sémaphores
défini est atteint, tous les appels client sont rejetés. Ce mode est préconisé pour les appels courts.

Le mode thread est le mode par défaut d’Hystrix. Il tire son avantage par son isolation du pool de threads de l’appelant.

74
CHANGEMENT DE VITESSE

75
Les Microservices
ÉLASTIQUE
Un Microservice doit pouvoir être déployé un nombre de fois qui varie en fonction de la demande, et ce,
indépendamment des autres services dans la même application.
RÉSILENT
Un Microservice doit échouer sans affecter d'autres services dans la même application.

Pourquoi utilise-t-on Docker ?

Distribution des applications facilitée.


Comportement identique des applications en
Dev/Qualif/Prod.
Déploiement, lancement et arrêt rapide.
Linux et Windows (en preview dans Windows Server 2016)
Permet de reconstruire un container à partir d'un simple
fichier Dockerfiles.
Gestion des containers avec peu d'outils, identique sur
toutes les plateformes.
Des API disponibles pour piloter l'ensemble depuis d'autres
applications.

76
SOLUTIONS D'ORCHESTRATION OPENSOURCE

Docker Machine/Compose/Swarm http://www.docker.com


Kubernetes http://k8s.io
Apache Mesos http://mesos.apache.org/
Openshift Origin v3 http://www.openshift.org
Rancher http://rancher.com
Kontena http://www.kontena.io/

77

Vous aimerez peut-être aussi