PREMIER MICRO-SERVICE AVEC LE FRAMEWORK SPRINGBOOT - ESPACE LIBRE LE SITE DE LA LICENCE ...
←
→
Transcription du contenu de la page
Si votre navigateur ne rend pas la page correctement, lisez s'il vous plaît le contenu de la page ci-dessous
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é, éfficacité … 6
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 (Fonctionne également avec le JDK 11) http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html La U202 est la dernière version du JDK8 qui sera disponible en patch libre les prochaines versions seront payante, il est donc conseillé de passer au JDK 11 avec les restrictions de la licence commerciale ou la version Open Source sous licence GPL https://jdk.java.net/11/ Penser à définir la variable JAVA_HOME correctement, Ainsi que votre PATH echo %JAVA_HOME% OR echo $JAVA_HOME echo %PATH% OR 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 com.ht.dev PremierMicroService 0.0.1 jar PremierMicroService Basic project for Spring Boot 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
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 Placer dans le fichier de config application.properties org.springframework.boot management.security.enabled=false spring-boot-starter-actuator 1.5.3.RELEASE 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; 3 Couche Persistance avec JDBCTemplate 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 persons = new ArrayList(); List 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 getByName(@PathVariable("cle") String cle,@PathVariable(« IPP") String IPP) { List patients=new ArrayList(); List 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"; Mapping de la variable } du Template HTML « bonjour.html » 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 Page Bonjour 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 de 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 2 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 service Front NetFlix Eureka discovery. Browser (2) Notre service FrontEnd va interroger le service Discovery pour connaitre Internet 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) https://github.com/univphf/ServerDiscovery.git •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 » org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-eureka-server 1.3.0.RELEASE 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
https://github.com/univphf/ClientDiscovery.git 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 » org.springframework.cloud spring-cloud-starter-eureka 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 https://github.com/univphf/ClientBackDiscovery.git 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 https://github.com/univphf/ClientFrontDiscovery.git 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 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
Les 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 pouvez aussi lire