@ DTSI
Ou le routage dit "intelligent" en environnements distribués.
Plus besoin de savoir où les services sont physiquement installés. Zuul a un double rôle routeur / répartiteur de charge et va s'appuyer sur l'annuaire de services Euréka pour propager les appels de services.
Ou comment réagir, en environnements distribués, à l'éventuelle indisponibilité d'un ou de plusieurs services tiers.
Hystrix est une implémentation du pattern Circuit Breaker. Il s'agit ici de continuer à rendre un service en mode dit dégradé dans vos applications lorsque le service original renvoie un trop grand nombre d'erreurs.
L'utilisation de Zuul est obligatoire lorsque votre application utilise des services tiers (externes à ladite application).
Les (micro-) services qui sont utilisés par plus d'une applications doivent obligatoirement s'enregistrer sur l'annuaire de services Euréka.
Dans un environnement tel que défini à la DTSI (sauf Spring Cloud Config) :
- Spring Boot Admin
- Spring Cloud Netflix Eureka (deux instances répliquées)
- Déploiement d'un micro-service exposant le référentiel ISO des pays, et donc référencé dans l'annuaire de service Euréka)
- Déploiement d'une application d'inscription : backend Spring Boot et frontend Angular
Le projet de démonstration est disponible ici.
Dans l'application utilisatrice de services transverses :
Ajouter les dépendances :
<dependencies>
(...)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
(...)
</dependencies>
Dans l'application utilisatrice de services transverses :
Modifier la classe de bootstrap de votre application en lui ajoutant les annotations :
@EnableDiscoveryClient
@EnableZuulProxy
Il y a plusieurs implémentations pour la découverte des services : eureka, consul, zookeeper...
Il est donc possible d'utiliser l'annotation générique @EnableDiscoveryClient (Spring Cloud Commons), ou l'annotation spécifique @EnableEurekaClient (Spring Cloud Netflix).
Dans l'application utilisatrice de services transverses :
Ajouter les éléments de configuration :
eureka:
client:
serviceUrl:
defaultZone: http://vbl-dva-xubuntu15.dev.gnc:10300/eureka/ \
,http://vbl-dva-xubuntu15.dev.gnc:10301/eureka/
region: dev.gnc
registerWithEureka: false
zuul:
ignoredServices: '*'
routes:
countries:
path: /api/v1/countries/**
serviceId: MS-Countries
strip-prefix: false
- Coupure du service du référentiel ISO 3166 des pays.
- Observation du comportement de l'application.
- Re-démarrage du référentiel des pays
- Re-démarrage de l'application cliente dans une version prenant en charge la protection de l'appel au référentiel des pays
- Observation du comportement de l'application
Mais c'est normal, puisque nous sommes revenus dans la situation d'origine : le service tiers est en fonctionnement...
Et si on coupe de nouveau ledit service ?
Dans l'application utilisatrice de services transverses :
Ajouter un Bean de Configuration qui implémente ZuulFallbackProvider.
Attention, ce n'est pas possible pour les versions de Spring - et dépendances associées - inférieures ou égales à Camden.SR1 !
package nc.dva.examples.hystrix;
import ...;
@Configuration
public class PlayerCountryFallbackConfiguration {
@Bean
public ZuulFallbackProvider zuulFallbackProvider() {
return new ZuulFallbackProvider() {
@Autowired
private PlayerRepository lPlayerRepository;
@Override
public String getRoute() {
// Might be confusing: it's the serviceId property and not the
// route!!!
// Be carefull !
return "MS-Countries";
}
@Override
public ClientHttpResponse fallbackResponse() {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.toString();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
List<String> localCountries = lPlayerRepository.findCountries();
List<Country> result = new ArrayList<Country>();
for (String codeIso3 : localCountries) {
Country lCountry = new Country(codeIso3, codeIso3);
result.add(lCountry);
}
return new ByteArrayInputStream(new Gson().toJson(result).getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccessControlAllowOrigin("*");
return headers;
}
};
}
};
}
}
package nc.dva.examples.hystrix;
public class Country {
private String codeIso3;
private String libelleCOG;
(...)
}
The icing on the cake !
Le résultat est ici assez pauvre, mais le tableau de bord permet, pour chaque service monitoré, de vérifier l'état du circuit - au sens du pattern Circuit Breaker.
Tout se passe dans l'application Spring Boot Admin !
Ajouter, dans le fichier pom.xml, la dépendance :
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-hystrix</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
Ajouter, dans le fichier de configuration de l'application :
spring:
boot:
admin:
routes:
endpoints: env,metrics,trace,dump,jolokia,info,configprops,trace,logfile,refresh, \
flyway,heapdump,loggers,auditevents,hystrix.stream
Sur la page About de Spring Boot Admin : vous devez trouver sba-applications-hystrix dans la liste Loaded ui modules.
Toujours dans Spring Boot Admin, sur la page Détail de l'application concernée, vous devez trouver un nouvel onglet Hystrix qui vous donne accès au tableau de bord Hystrix (cf. copie d'écran supra).