Bonjour Ă tous!
Dans cet article, nous allons démontrer les composants de base pour créer des microservices RESTful à l'aide du registre des services Consul, Spring Boot pour tous les échafaudages, injection de dépendance, Maven pour l'assemblage, ainsi que Spring REST et l'API Java RESTful Jersey / JaxRS.
Les principaux avantages des microservices:
- Les microservices vous aident Ă assouplir votre code
- Les microservices permettent à différentes équipes de travailler sur de petits composants à l'aide de technologies indépendantes, offrant un déploiement plus sécurisé et plus fréquent. Spring Boot prend en charge diverses implémentations pour créer une API REST
- La découverte et l'appel de services ne dépendent pas de la plateforme de service
- Swagger crée une documentation API solide et une interface d'appel
Si vous n'utilisez pas déjà les microservices, vous n'avez pas réussi à entrer dans la phase des premiers adeptes sur la courbe de perception technologique, et c'est probablement le bon moment pour commencer.

Au cours des deux derniÚres décennies, l'entreprise est devenue trÚs flexible dans notre processus SDLC, mais nos applications, en rÚgle générale, restent encore monolithiques, avec d'énormes pots prenant en charge toutes les différentes API et versions sur le marché. Mais à l'heure actuelle, on souhaite davantage de processus Lean et DevOps, et la fonctionnalité devient «sans serveur». La refactorisation vers les microservices peut réduire l'intrication du code et des ressources, rendre les assemblages plus petits, les versions plus sûres et les API plus stables.
Dans cet article, nous allons créer une application simple de gestion de portefeuille boursier que les clients peuvent appeler pour évaluer leur portefeuille d'actions (tickers et valeurs boursiÚres). Le microservice de portefeuille récupérera le portefeuille du client, l'enverra au microservice de tarification pour appliquer les derniers prix, puis rendra le portefeuille pleinement évalué et sous-totalisé, démontrant tout cela par le biais d'un appel de repos.

Avant de commencer à travailler sur la création de nos microservices, préparons notre environnement en créant Consul.
Télécharger Consul
Nous utiliserons le consul Hashicorp pour découvrir les services, alors allez sur
www.consul.io/downloads.html et téléchargez Consul pour Windows, Linux, Mac, etc. Cela vous fournira un fichier exécutable que vous devez ajouter à votre chemin.
Lancer Consul
Ă l'invite de commandes, lancez Consul en mode dev:
consul agent -dev
Pour vérifier qu'il fonctionne, accédez à votre navigateur et accédez à l'interface consul
http: // localhost: 8500 . Si tout se passe bien, le consul doit signaler qu'il est bel et bien vivant. En cliquant sur le service consul (à gauche), vous recevrez des informations supplémentaires (à droite).

En cas de problÚme pour le moment, assurez-vous d'ajouter Consul au chemin d'exécution et les ports 8500 et 8600 sont disponibles.
Créer une application SpringBoot
Nous utiliserons le
Spring Initializr , qui est intégré dans la plupart des IDE, pour échafauder nos applications SpringBoot. Les captures d'écran ci-dessous utilisent IntelliJ IDEA.
Sélectionnez «Fichier / Nouveau projet» pour ouvrir un nouveau modÚle de projet, puis «Spring Initializr».

En général, vous pouvez configurer un échafaudage sans IDE en remplissant un formulaire en ligne via la page Web SpringBoot Initializr
start.spring.io , qui crĂ©era un fichier zip pour votre projet vide, prĂȘt Ă ĂȘtre tĂ©lĂ©chargĂ©.
Cliquez sur «Suivant» et remplissez les métadonnées du projet. Utilisez la configuration suivante:

Cliquez sur «Suivant» pour sélectionner les dépendances, puis entrez «Jersey» et «Consul Discovery» dans la recherche de dépendances. Ajoutez ces dépendances:

Cliquez sur «Suivant» pour indiquer le nom du projet et son emplacement. Conservez le nom par défaut «portfolio» et spécifiez l'emplacement préféré du projet, puis cliquez sur «terminer» pour créer et ouvrir le projet:

Nous pouvons utiliser le fichier application.properties généré, mais SpringBoot reconnaßt également le format YAML, qui est un peu plus facile à visualiser, nous allons donc le renommer en application.yml.
Nous appelons le microservice «portfolio-service». Nous pouvons spécifier un port ou utiliser le port 0 pour que l'application utilise un port disponible. Dans notre cas, nous utiliserons le 57116. Si vous hébergez ce service en tant que conteneur Docker, vous pouvez le mapper sur n'importe quel port que vous sélectionnez. Nommez l'application et spécifiez notre port en ajoutant ce qui suit à notre application.yml:
spring: application: name: portfolio-service server: port: 57116
Pour rendre notre service disponible, ajoutez une annotation à notre classe d'application SpringBoot. Ouvrez l'application PortfolioApplication et ajoutez @EnableDiscoveryClient au-dessus de la déclaration de classe.
Confirmez les importations. La classe devrait ressembler Ă ceci:
package com.restms.demo.portfolio; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; . . . @SpringBootApplication @EnableDiscoveryClient public class PortfolioApplication { public static void main(String[] args) { SpringApplication.run(PortfolioApplication.class, args); } }
(Pour montrer comment les microservices peuvent ĂȘtre constituĂ©s de plates-formes indĂ©pendantes, nous utiliserons Jersey pour ce service et Spring REST pour le suivant).
Pour configurer le service Web RESTful sur Jersey, nous devons spĂ©cifier la classe de configuration ResourceConfig. Ajoutez la classe JerseyConfig (pour dĂ©monstration, nous l'enregistrerons dans le mĂȘme package que notre classe d'application). Il devrait ressembler Ă ceci, plus le package et l'importation corrects:
@Configuration @ApplicationPath("portfolios") public class JerseyConfig extends ResourceConfig { public JerseyConfig() { register(PortfolioImpl.class); } }
Notez qu'il hérite de ResourceConfig pour le désigner comme classe de configuration Jersey. L'attribut @ApplicationPath ("portfolios") définit le contexte de l'appel, ce qui signifie que les appels doivent commencer par l'élément de chemin "portfolioios". (Si vous l'omettez, le contexte par défaut est «/»).
La classe PortfolioImpl servira deux demandes: portfolios / client / {id-client} renvoie tous les portefeuilles et portefeuilles / client / {id-client} / portfolio / {id-portfolio} renvoie un portefeuille. Un portefeuille se compose d'un ensemble de tickers et du nombre d'actions détenues par ce ticker. (Pour démonstration, il existe trois clients avec les identificateurs 0, 1 et 2, chacun ayant trois portefeuilles avec les identificateurs 0, 1 et 2).
Votre IDE vous demandera de crĂ©er PortfolioImpl; faites-le maintenant. Pour le dĂ©montrer, ajoutez-le au mĂȘme package. Entrez le code ci-dessous et confirmez toutes les importations:
@Component @Path("/") public class PortfolioImpl implements InitializingBean { private Object[][][][] clientPortfolios; @GET @Path("customer/{customer-id}") @Produces(MediaType.APPLICATION_JSON) // a portfolio consists of an array of arrays, each containing an array of // stock ticker and associated shares public Object[][][] getPortfolios(@PathParam("customer-id") int customerId) { return clientPortfolios[customerId]; } @GET @Path("customer/{customer-id}/portfolio/{portfolio-id}") @Produces(MediaType.APPLICATION_JSON) public Object[][] getPortfolio(@PathParam("customer-id") int customerId, @PathParam("portfolio-id") int portfolioId) { return getPortfolios(customerId)[portfolioId]; } @Override public void afterPropertiesSet() throws Exception { Object[][][][] clientPortfolios = { { // 3 customers, 3 portfolios each {new Object[]{"JPM", 10201}, new Object[]{"GE", 20400}, new Object[]{"UTX", 38892}}, {new Object[]{"KO", 12449}, new Object[]{"JPM", 23454}, new Object[]{"MRK", 45344}}, {new Object[]{"WMT", 39583}, new Object[]{"DIS", 95867}, new Object[]{"TRV", 384756}}, }, { {new Object[]{"GE", 38475}, new Object[]{"MCD", 12395}, new Object[]{"IBM", 91234}}, {new Object[]{"VZ", 22342}, new Object[]{"AXP", 385432}, new Object[]{"UTX", 23432}}, {new Object[]{"IBM", 18343}, new Object[]{"DIS", 45673}, new Object[]{"AAPL", 23456}}, }, { {new Object[]{"AXP", 34543}, new Object[]{"TRV", 55322}, new Object[]{"NKE", 45642}}, {new Object[]{"CVX", 44332}, new Object[]{"JPM", 12453}, new Object[]{"JNJ", 45433}}, {new Object[]{"MRK", 32346}, new Object[]{"UTX", 46532}, new Object[]{"TRV", 45663}}, } }; this.clientPortfolios = clientPortfolios; } }
L'annotation de
composant la désigne comme classe du composant Spring et l'expose comme point de terminaison.
Les annotations de
chemin sur la déclaration de classe déclarent que la classe est accessible via l'élément de chemin «/», et deux appels api pris en charge sont disponibles via portfolios / customer / {customer-id} et portfolios / customer / {customer-id} / portfolio / {portfolio- id}, comme nous le voyons dans les annotations de la méthode. Notez que le chemin ("/") est le chemin par défaut, mais nous le laissons pour référence. Les méthodes sont désignées comme HTTP GET via @GETannotation. Notre méthode est conçue pour renvoyer un tableau et annotée pour renvoyer Json, elle renvoie donc un tableau Json. Remarquez comment les annotations
Path Param sont utilisĂ©es dans la signature de mĂ©thode pour extraire les paramĂštres affichĂ©s des requĂȘtes affichĂ©es.
(Pour notre démo, nous retournons des valeurs codées en dur. Bien sûr, dans la pratique, l'implémentation interrogera la base de données ou un autre service ou source de données au lieu du code dur.)
Créez maintenant un projet et exécutez-le. Si vous utilisez IntelliJ, il créera un exécutable par défaut, il vous suffit donc de cliquer sur la flÚche verte «exécuter». Vous pouvez également utiliser
mvn spring-boot:run
Ou vous pouvez effectuer l'installation de maven et exécuter l'application à l'aide de java -jar, en pointant vers le fichier jar généré dans le répertoire cible:
java -jar target\portfolio-0.0.1-SNAPSHOT.jar
Maintenant, nous devrions voir ce service dans Consul, alors revenons à notre navigateur, téléchargez
http: // localhost: 8500 / ui / # / dc1 / services (ou mettez Ă jour si vous y ĂȘtes dĂ©jĂ ).

Hmm, nous voyons notre service là -bas, mais il est affiché comme ayant échoué. C'est parce que Consul attend un signal de rythme cardiaque «sain» de notre service.
Pour générer des signaux de pulsation, nous pouvons ajouter une dépendance sur le service
Spring Actuator au pom de notre application.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
Pendant que nous sommes à pom, notons qu'il existe un conflit de version avec Jersey entre le starter Consul et le starter Jersey. Pour lisser cela, désigner le démarreur Jersey comme la premiÚre dépendance.
Votre pom devrait maintenant contenir les dépendances suivantes:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
En redémarrant Consul, le service Portfolio affiche un heureux:

Maintenant, dans le service de portefeuille, il y a deux nĆuds de transmission: l'un d'eux est notre implĂ©mentation du service de portefeuille, et l'autre est le rythme cardiaque.
Vérifions le port qui a été attribué. Vous pouvez le voir dans la sortie de l'application:
INFO 19792 --- [ main] sbcetTomcatEmbeddedServletContainer : Tomcat started on port(s): 57116 (http)
Vous pouvez également voir le port directement dans l'interface utilisateur du Consul. Cliquez sur «service client», puis sélectionnez le lien «Lien de vérification du service client», qui affiche le port de service, dans ce cas 57116.

Demandez
http: // localhost: 57116 / portfolios / customer / 1 / portfolio / 2 et vous verrez le tableau json [["IBM", 18343], ["DIS", 45673], ["AAPL", 23456]]
Notre premier microservice est ouvert aux affaires!
Service de tarification
Ensuite, nous créerons notre service de tarification, cette fois en utilisant Spring RestController au lieu de Jersey.
Le service de tarification acceptera l'identifiant client et l'identifiant de portefeuille comme paramÚtres et utilisera RestTemplate pour demander des services de portefeuille, recevoir des tickers et des stocks, et retourner les prix actuels. (Je n'ai pas besoin de vous dire que ces valeurs sont de fausses nouvelles, alors ne les utilisez pas pour prendre des décisions commerciales!)
Créez un nouveau projet en utilisant les informations suivantes:

Cette fois, sélectionnez les dépendances Web, Consul Discovery et Actuator:

Laissez le nom par défaut du projet «pricing» et créez un projet dans le répertoire de votre choix.
Cette fois, nous utiliserons application.properties au lieu de application.yml.
Définissez le nom et le port dans application.properties comme suit:
spring.application.name=pricing server.port=57216
Annoter PricingApplication avec @EnableDiscoveryClient. La classe devrait ressembler Ă ceci, plus le package et l'importation.
@SpringBootApplication @EnableDiscoveryClient public class PricingApplication { public static void main(String[] args) { SpringApplication.run(PricingApplication.class, args); } }
Ensuite, nous crĂ©erons la classe PricingEndpoint. Ici, je vais donner un exemple plus dĂ©taillĂ©, car il prĂ©sente plusieurs fonctions importantes, y compris la dĂ©couverte de services (recherche de services de portefeuille) et l'utilisation de RestTemplate pour la requĂȘte:
@RestController @RequestMapping("/pricing") public class PricingEndpoint implements InitializingBean { @Autowired DiscoveryClient client; Map<String, Double> pricingMap = new HashMap<>(); RestTemplate restTemplate = new RestTemplate(); @GetMapping("/customer/{customer-id}/portfolio/{portfolio-id}") public List<String> getPricedPortfolio( @PathVariable("customer-id") Integer customerId, @PathVariable("portfolio-id") Integer portfolioId) { List<ServiceInstance> instances = client.getInstances("portfolio-service"); ServiceInstance instance = instances.stream() .findFirst() .orElseThrow(() -> new RuntimeException("not found")); String url = String.format("%s/portfolios/customer/%d/portfolio/%d", instance.getUri(), customerId, portfolioId); // query for the portfolios, returned as an array of List // of size 2, containing a ticker and a position (
Pour trouver un service de portefeuille, nous devons avoir accĂšs Ă DiscoveryClient. Il est facile d'obtenir l'annotation @Autowired de Spring.
@Autowired DiscoveryClient client;
Cette instance DiscoveryClient est ensuite utilisée pour rechercher le service dans l'appel:
List<ServiceInstance> instances = client.getInstances("portfolio-service"); ServiceInstance instance = instances.stream().findFirst().orElseThrow(() -> new RuntimeException("not found"));
Une fois le service trouvé, nous pouvons l'utiliser pour répondre à notre demande, que nous composons conformément à l'appel API créé dans notre service de portefeuille.
String url = String.format("%s/portfolios/customer/%d/portfolio/%d", instance.getUri(), customerId, portfolioId);
Enfin, nous utilisons RestTemplate pour exécuter notre demande GET.
Object[] portfolio = restTemplate.getForObject(url, Object[].class);
Notez que pour les contrÎleurs Rest (ainsi que pour Spring MVC Request Controller), les variables de chemin sont récupérées à l'aide de l'annotation
Path Variable, contrairement Ă Jersey, qui, comme nous l'avons vu, utilise
Path Param.
Ceci conclut notre tarification avec Spring RestController.
La documentation
Nous avons résolu tous ces problÚmes afin de créer nos microservices, mais ils n'apporteront pas suffisamment d'avantages si nous ne donnons pas au monde des connaissances sur la façon de les utiliser.
Pour ce faire, nous utilisons l'outil
Swagger pratique et facile à utiliser, qui documente non seulement nos appels API, mais fournit également un client Web pratique pour les appeler.
Précisons d'abord Swagger dans notre pom:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
Ensuite, nous devons dire à Swagger laquelle de nos classes nous voulons documenter. Présentons la nouvelle classe SwaggerConfig contenant la spécification Swagger.
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.regex("/pricing.*")) .build(); } }
Voyons voir ce que fait cette classe. Tout d'abord, nous l'avons désigné comme une configuration Swagger avec l'annotation @ EnableSwagger2.
Ensuite, nous avons créé un composant Docket qui indique Ă Swagger quelles API doivent ĂȘtre affichĂ©es. Dans l'exemple ci-dessus, nous avons demandĂ© Ă Swagger de montrer tout chemin commençant par «/ pricing». Une alternative serait de spĂ©cifier des classes pour la documentation, et non pour les chemins:
.apis(RequestHandlerSelectors.basePackage("com.restms.demo")) .paths(PathSelectors.any())
Redémarrez le microservice de prix et appelez
http: // localhost: 57216 / swagger-ui.html Ă partir du navigateur

Cliquez sur Lister les opérations pour afficher les opérations de service en détail.
Cliquez sur Développer les opérations pour créer une demande basée sur le formulaire. Définissez certains paramÚtres, cliquez sur "Essayez-le!" et attendez la réponse:

Vous pouvez ajouter beaucoup plus de couleurs en ajoutant des annotations Swagger à vos méthodes.
Par exemple, décorez la méthode PricingImpl.getPricedPortfolio existante à l'aide de l'annotation @ApiOperation, comme illustré ci-dessous:
@ApiOperation(value = "Retrieves a fully priced portfolio", notes = "Retrieves fully priced portfolio given customer id and portfolio id") @GetMapping("/customer/{customer-id}/portfolio/{portfolio-id}") public List<String> getPricedPortfolio(@PathVariable("customer-id") Integer customerId, @PathVariable("portfolio-id") Integer portfolioId)
Rechargez et mettez Ă jour swagger-ui pour voir la nouvelle documentation mise Ă jour:

Et ce n'est pas tout ce que vous pouvez faire avec Swagger, alors consultez la documentation.
Yuri Dvorzhetsky , un conférencier dans notre cours
"Developer on the Spring Framework", vous en dira plus sur Spring Boot:
Article original