Introduction
Initialement, Spring Cloud utilisait Netflix Zuul v1 comme serveur périphérique. Cependant, depuis la sortie de Spring Cloud Greenwich, il est recommandé d'utiliser Spring Cloud Gateway à la place.
Spring Cloud Gateway est livré avec une prise en charge similaire pour les fonctionnalités critiques telles que le routage basé sur le chemin d'URL et la protection des points de terminaison via l'utilisation d'OAuth 2.0 et d'OpenID Connect (OIDC).
Une différence importante entre Netflix Zuul v1 et Spring Cloud Gateway est que Spring Cloud Gateway est basé sur des API non bloquantes qui utilisent Spring 5, Project Reactor et Spring Boot 2, tandis que Netflix Zuul v1 est basé sur des API bloquantes. Cela signifie que Spring Cloud Gateway devrait être capable de gérer un plus grand nombre de requêtes simultanées que Netflix Zuul v1, ce qui est important pour un serveur périphérique par lequel passe tout le trafic externe.
Le diagramme suivant montre comment toutes les demandes de clients externes passent par Spring Cloud Gateway en tant que serveur périphérique. En fonction des chemins d'URL, il achemine les requêtes vers le microservice prévu :
Dans le diagramme précédent, nous pouvons voir comment le serveur Edge enverra des requêtes externes dont le chemin d'URL commence par /product-composite/ au microservice Product-Composite. Les services de base Product, Recommendation et Review ne sont pas accessibles à partir de clients externes.
Pour gérer la configuration d'un paysage système de microservices, Spring Cloud contient Spring Cloud Config, qui assure la gestion centralisée des fichiers de configuration en fonction des besoins.
Spring Cloud Config prend en charge le stockage des fichiers de configuration dans un certain nombre de backends différents, tels que :
- Un référentiel Git, par exemple, sur GitHub ou Bitbucket
- Un système de fichiers local
- Coffre-fort HashiCorp
- Une base de données JDBC
Spring Cloud Config permet de gérer la configuration dans une structure hiérarchique. Par exemple, nous pouvons placer les parties communes de la configuration dans un fichier commun et les paramètres spécifiques au microservice dans des fichiers de configuration séparés.
Spring Cloud Config détecte les modifications de la configuration et envoie des notifications aux microservices concernés. Il utilise Spring Cloud Bus pour transporter les notifications. Spring Cloud Bus est une abstraction au-dessus de Spring Cloud Stream qui prend en charge l'utilisation de RabbitMQ ou de Kafka comme système de messagerie pour le transport des notifications prêtes à l'emploi.
Le diagramme suivant illustre la coopération entre Spring Cloud Config, ses clients, un référentiel Git et Spring Cloud Bus :
Ce diagramme montre que lorsqu'un microservice démarre, il demande sa configuration au serveur de configuration. Le serveur de configuration obtient la configuration à partir d'un dépôt Git configuré.
Le référentiel Git peut être configuré pour envoyer des notifications au serveur de configuration lorsque des validations Git sont transmises au référentiel Git.
Le serveur de configuration publie des événements de modification à l'aide de Spring Cloud Bus. Les microservices concernés par la modification récupèrent leur configuration mise à jour à partir du serveur de configuration.
Enfin, Spring Cloud Config prend également en charge le chiffrement des informations sensibles dans la configuration, telles que les informations d'identification.
Ajouter spring gateway
Dans cette section, nous verrons comment le serveur de gateway est ajouté au système et comment il affecte la façon dont les clients externes accèdent aux API publiques exposées par les microservices.
Toutes les requêtes entrantes seront désormais acheminées via le serveur Edge, comme illustré par le schéma suivant :
Comme nous pouvons le voir sur le schéma précédent, les clients externes envoient toutes leurs requêtes au serveur Edge. Le serveur Edge peut acheminer les demandes entrantes en fonction du chemin de l'URL. Par exemple, les demandes dont l'URL commence par /product-composite/ sont acheminées vers le microservice composite du produit, et une demande dont l'URL commence par /eureka/ est acheminée vers le serveur de découverte basé sur Netflix Eureka.
Configurer la Gateway en tant que serveur périphérique est simple et peut être réalisé en procédant comme suit :
- Créez un projet Spring Boot à l'aide de Spring Initializr.
- Ajoutez une dépendance sur spring-cloud-starter-gateway.
- Pour pouvoir localiser les instances de microservice via Netflix Eureka, ajoutez également la dépendance spring-cloud-starter-netflix-eureka-client.
- Ajoutez le projet de serveur Edge au fichier settings.gradle :
include ':spring-cloud:gateway'
- Ajouter un Dockerfile avec le même contenu que pour les microservices
- Ajoutez le serveur Edge à nos fichiers Docker Compose :
gateway:
environment:
- SPRING_PROFILES_ACTIVE=docker
build: spring-cloud/gateway
mem_limit: 512m
ports:
- "8080:8080"
Étant donné que le serveur de périphérie gérera tout le trafic entrant, nous déplacerons la vérification de l'état composite du service composite du produit vers le serveur de périphérie.
Ensuite, vous pouvez ajouter une configuration pour les règles de routage et plus encore. Comme il y a beaucoup à configurer, cela est traité dans une section distincte.
Ajout d'une vérification d'état composite
Avec un serveur Edge en place, les demandes de vérification de l'état externe doivent également passer par le serveur périphérique. Par conséquent, la vérification de l'état composite qui vérifie l'état de tous les microservices a été déplacée du service composite vers le serveur Edge.
Les éléments suivants ont été ajoutés au serveur Edge :
- La classe HealthCheckConfiguration a été ajoutée, qui déclare le contributeur de santé réactif :
@Bean
ReactiveHealthContributor healthcheckMicroservices() {
final Map<String, ReactiveHealthIndicator> registry =
new LinkedHashMap<>();
registry.put("product", () ->
getHealth("http://product"));
registry.put("recommendation", () ->
getHealth("http://recommendation"));
registry.put("review", () ->
getHealth("http://review"));
registry.put("product-composite", () ->
getHealth("http://product-composite"));
return CompositeReactiveHealthContributor.fromMap(registry);
}
private Mono<Health> getHealth(String baseUrl) {
String url = baseUrl + "/actuator/health";
LOG.debug("Setting up a call to the Health API on URL: {}",
url);
return webClient.get().uri(url).retrieve()
.bodyToMono(String.class)
.map(s -> new Health.Builder().up().build())
.onErrorResume(ex ->
Mono.just(new Health.Builder().down(ex).build()))
.log(LOG.getName(), FINE);
}
- La classe d'application principale, GatewayApplication, déclare un bean WebClient.Builder à utiliser par l'implémentation de l'indicateur de santé comme suit :
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
Avec un bilan de santé en place pour le serveur de périphérie, nous sommes prêts à examiner la configuration qui doit être configurée pour Spring Cloud Gateway.
Configuration d'une passerelle Spring Cloud
Lorsqu'il s'agit de configurer un Spring Cloud Gateway, le plus important est de configurer les règles de routage. Nous devons également configurer quelques autres éléments dans la configuration :
-
Étant donné que Spring Cloud Gateway utilisera Netflix Eureka pour trouver les microservices vers lesquels il acheminera le trafic, il doit être configuré en tant que client Eureka de la même manière que vous avez dans le git (sidebar).
-
Configurez Spring Boot Actuator pour une utilisation en développement :
management.endpoint.health.show-details: "ALWAYS"
management.endpoints.web.exposure.include: "*"
- Configurez les niveaux de journalisation afin que nous puissions voir les messages de journalisation des parties intéressantes du traitement interne dans Spring Cloud Gateway, par exemple, comment il décide où acheminer les demandes entrantes vers :
logging:
level:
root: INFO
org.springframework.cloud.gateway.route.
RouteDefinitionRouteLocator: INFO
org.springframework.cloud.gateway: TRACE
La configuration des règles de routage peut être effectuée de deux manières : par programmation, à l'aide d'un DSL Java ou par configuration. L'utilisation d'un DSL Java pour configurer des règles de routage par programme peut être utile dans les cas où les règles sont stockées dans un stockage externe, comme une base de données, ou sont données au moment de l'exécution, par exemple, via une API RESTful ou un message envoyé à la passerelle. Dans des cas d'utilisation plus statiques, je trouve plus pratique de déclarer les routes dans le fichier de configuration, src/main/resources/application.yml. La séparation des règles de routage du code Java permet de mettre à jour les règles de routage sans avoir à déployer une nouvelle version du microservice.
Un parcours est défini par ce qui suit :
- Prédicats, qui sélectionnent une route en fonction des informations contenues dans la requête HTTP entrante
- Filtres, qui peuvent modifier à la fois la requête et/ou la réponse
- Un URI de destination, qui décrit où envoyer une requête
- Un ID, c'est-à-dire le nom de la route
Routage des requêtes vers l'API composite:
Si nous souhaitons diriger les demandes entrantes dont le chemin de l'URL commence par /product-composite/ vers notre service product-composite, nous pouvons définir une règle de routage comme suit :
spring.cloud.gateway.routes:
- id: product-composite
uri: lb://product-composite
predicates:
- Path=/product-composite/**
Quelques points à noter du code précédent sont :
id: product-composite
: Le nom de l'itinéraire est "product-composite".uri: lb://product-composite
: Si la route est sélectionnée par ses prédicats, la requête sera acheminée vers le service qui est nommé "product-composite" dans le service de découverte, Netflix Eureka. Le protocolelb://
est utilisé pour ordonner à Spring Cloud Gateway d'utiliser l'équilibreur de charge côté client pour rechercher la destination dans le service de découverte.predicates: - Path=/product-composite/**
est utilisé pour spécifier à quelles requêtes cette route doit correspondre.**
correspond à zéro ou plusieurs éléments dans le chemin.
- id: product-composite-swagger-ui
uri: lb://product-composite
predicates:
- Path=/openapi/**
Si une demande est envoyée au serveur périphérique avec un URI qui commence par /openapi/, elle sera redirigée vers le service product-composite. Cela peut être accompli en définissant une règle de routage appropriée dans la configuration de Spring Cloud Gateway.
Routage des requêtes vers l'API et la page Web du serveur Eureka:
Pour permettre une séparation claire entre l'API et la page Web d'Eureka, Eureka expose à la fois une API et une page Web pour ses clients. Pour acheminer correctement les requêtes entrantes, nous devons mettre en place les routes comme suit :
- Les requêtes envoyées au serveur Edge avec le chemin commençant par
/eureka/api
doivent être traitées comme un appel à l'API Eureka. - Les requêtes envoyées au serveur périphérique avec le chemin commençant par
/eureka/web
doivent être traitées comme un appel à la page Web Eureka.
La requête API sera acheminée vers http://${app.eureka-server}:8761/eureka
. La règle de routage pour l'API Eureka ressemble à ceci :
- id: eureka-api
uri: http://${app.eureka-server}:8761
predicates:
- Path=/eureka/api/{segment}
filters:
- SetPath=/eureka/{segment}
La partie {segment} de la valeur Path correspond à zéro ou plusieurs éléments dans le chemin et sera utilisée pour remplacer la partie {segment} dans la valeur SetPath.
Les demandes de pages Web seront acheminées vers http://${app.eureka-server}:8761. La page Web chargera plusieurs ressources Web, telles que les fichiers .js, .css et .png. Ces demandes seront acheminées vers http://${app.eureka-server}:8761/eureka. Les règles de routage pour la page Web Eureka ressemblent à ceci :
- id: eureka-web-start
uri: http://${app.eureka-server}:8761
predicates:
- Path=/eureka/web
filters:
- SetPath=/
- id: eureka-web-other
uri: http://${app.eureka-server}:8761
predicates:
- Path=/eureka/**
De la configuration précédente, nous pouvons prendre les notes suivantes. La propriété ${app.eureka-server} est résolue par le mécanisme de propriété de Spring en fonction du profil Spring activé :
- Lors de l'exécution des services sur le même hôte sans utiliser Docker, par exemple, à des fins de débogage, la propriété sera traduite en localhost en utilisant le profil par défaut.
- Lors de l'exécution des services en tant que conteneurs Docker, le serveur Netflix Eureka s'exécute dans un conteneur portant le nom DNS eureka. Par conséquent, la propriété sera traduite en eureka en utilisant le profil Docker.
Les parties pertinentes du fichier application.yml sont :
app.eureka-server: localhost
---
spring.config.activate.on-profile: docker
Routage des requêtes avec prédicats et filtres
Configuration pour router les requêtes avec des prédicats et des filtres dans Spring Cloud Gateway
Itinéraire pour renvoyer 200 OK pour les appels à i.feel.lucky:8080/headerrouting
- id: host_route_200
uri: http://httpstat.us
predicates:
- Host=i.feel.lucky:8080
- Path=/headerrouting/**
filters:
- SetPath=/200
Itinéraire pour renvoyer 418 I'm a teapot pour les appels à im.a.teapot:8080/headerrouting
- id: host_route_418
uri: http://httpstat.us
predicates:
- Host=im.a.teapot:8080
- Path=/headerrouting/**
filters:
- SetPath=/418
Itinéraire pour renvoyer 501 Not Implemented pour tous les autres appels à headerrouting
- id: host_route_501
uri: http://httpstat.us
predicates:
- Path=/headerrouting/**
filters:
- SetPath=/501
Ce code montre comment configurer les itinéraires avec des prédicats et des filtres dans Spring Cloud Gateway. Nous avons trois itinéraires différents pour les noms d'hôte spécifiques et les chemins d'accès à headerrouting. Nous utilisons le prédicat Host pour sélectionner les demandes avec des noms d'hôte spécifiques et le filtre SetPath pour définir le code HTTP souhaité dans le chemin de la demande. Cela permet de router les demandes vers la bonne réponse en fonction des critères de prédicats et de filtres définis dans les itinéraires.
Tester les règles de routage
- Appel de l'API composite du produit via le serveur Edge
- Suivre la sortie de journal du serveur Edge
docker-compose logs -f --tail=0 gateway
- Appeler l'API composite du produit via le serveur Edge
curl http://localhost:8080/product-composite/1
-
Attendre une réponse normale de l'API composite du produit
-
Trouver les informations suivantes dans la sortie du journal du serveur Edge
-
Il transmet la demande à http://b8013440aea0:8080/product-composite/1.
Appel de l'API Eureka via le serveur Edge
- Appeler l'API Eureka via le serveur Edge pour voir quelles instances sont actuellement enregistrées sur le serveur de découverte
curl -H "accept:application/json" localhost:8080/eureka/api/apps -s | jq -r .applications.application[].instance[].instanceId
-
Attendre une réponse de la liste des instances disponibles
-
Ouvrir la page Web Eureka dans un navigateur Web en utilisant l'URL http://localhost:8080/eureka/web
-
Vérifier que les instances signalées par l'API et la page Web Eureka correspondent