logo le blog invivoo blanc

Bilan de santé (Health Check) avec – Springboot Actuator

20 mai 2021 | Design & Code, Java | 1 comment

Actuator – Avec une architecture micro-service notre application fait appel à plusieurs services de type REST ou SOAP, à peu près une vingtaine de services répartis et gérés par différentes équipes.

Pour assurer le bon fonctionnement de tous ces services nous avons besoin de surveiller et gérer efficacement toutes les dépendances externes.

SpringBoot fournit une librairie (ACTUATOR) très efficace et simple à configurer qui nous permet de mettre en place rapidement une surveillance détaillée de toutes les dépendances externes utilisées.

I. Présentation

Littéralement, cela représente l’état de santé global de l’application. Techniquement, c’est une ressource RESTful normalisée fournissant un état global UP/DOWN et un état de chacune des dépendances vitales.

II. Objectif

Ce que nous cherchons à faire est :

  • Un état de santé global de l’application.
  • Vérifier la disponibilité de tous les services consommés.
  • Vérifier l’accessibilité des comptes techniques.
  • Vérifier la configuration, par exemple dans notre cas il s’agissait de vérifier l’existence des templates des mails.
  • Afficher un état de santé global par dépendance, sachant qu’une dépendance représente un appel vers un ou plusieurs services.

III. Configuration

La configuration et l’utilisation d’Actuator est un peu différente entre la version 1 et 2 de Springboot.

Ici, nous utilisons la version 2.1 de Springboot et nous supposons que la configuration automatique (@SpringBootApplication) est déjà activée, pour plus d’informations je vous recommande de consulter mon article sur l’Auto-configuration avec SpringBoot.

Pour utiliser ACTUATOR il suffit d’ajouter la dépendance dans le pom.xml

Maven dependence

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Un état de santé global

Le fait d’ajouter cette dépendance suffit pour que vous obteniez un endPoint de vérification d’état de santé global : http://mon.host.com/mon_application/actuator/health

Vérifier la disponibilité de tous les services appelés

Nous appelons plusieurs services répartis dans différentes équipes, nous avons besoin d’être alertés de l’indisponibilité de l’un de ces services afin de pouvoir informer nos fournisseurs et demander le rétablissement du service et aussi nos consommateurs afin de les orienter vers d’autres solutions alternatives.

Généralement une application a besoin d’un ou plusieurs services, fournis par d’autres applications, par exemple :

Pour checker par exemple l’état de santé d’un service A, nous devons ajouter un nouveau bean qui étendra HealthIndicator. Ce composant, suite à un appel, se chargera d’émettre une requête http vers le service cible, et vérifier qu’on ne reçoit pas une réponse de type 503 (unavailable service).

ServiceAHealthIndicator

package mon.application.healthcheck;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;

import static org.springframework.http.HttpStatus.SERVICE_UNAVAILABLE;

@Component
public class ServiceAHealthCheck implements HealthIndicator {

    @Value("${dependance1.service.a.endpoint}")
    private String serviceAEndPoint;

    @Autowired
    private HealthIndicatorService healthIndicatorService;

    @Override
    public Health health() {
        try {
            Object myRequest = createRequest();
            webServiceTemplate.marshalSendAndReceive(serviceAEndPoint, myRequest);
        } catch (HttpClientErrorException | HttpServerErrorException e) {
            if (SERVICE_UNAVAILABLE == e.getStatusCode()) {
                return Health.down(e).withDetail("URL", serviceAEndPoint).withDetail("Request", myRequest).build();
            }
            return Health.up().withDetail("URL", serviceAEndPoint).withDetail("Request", myRequest).build();
        } catch (MyCustomException e) {
            return Health.status("WARNING").withDetail("URL", serviceAEndPoint).withDetail("Request", myRequest).withException(e).build();
        } catch (Exception e) {
            return Health.down(e).withDetail("URL", serviceAEndPoint).withDetail("Request", myRequest).build();
        }
        return Health.up().withDetail("URL", serviceAEndPoint).withDetail("Request", myRequest).build();
    }
}

Maintenant nous avons l’état de santé du ServiceA qui s’ajoute :

SERVICEA

"status" : "UP",
"details" : {
  "ServiceA" : {
    "status" : "UP",
    "details" : {
      "URL" : "http:// dependance1.service.a.endpoint ",
      "Request" : {
        "Property" : "value"
      }
    }
  }
}

Vérifier l’accessibilité des comptes techniques

Cette configuration est similaire à la précédente, il faut juste ajouter un nouveau bean qui implémente HealthIndicator; mais pour ce besoin il suffit de vérifier que la réponse retournée n’est pas un 401 UNAUTHORIZED.

Vérifier l’existence des configurations

La vérification de l’existence des configurations nécessaires pour utiliser les ServiceE, par exemple, ce fait de façon similaire en créant un bean, implémentant HealthIndicator, qui va récupérer la liste des configurations disponibles et valider qu’elles sont complètes.

Afficher un état de santé global par dépendance

Pour surveiller le bon fonctionnement et la disponibilité de tout une dépendance, la configuration requise est comme suit :

Créer une classe de configuration qui utilise le composant HealthAggregator comme l’exemple qui suit :

@Configuration
public class Dependance2HealthCheck {



    @Value("${dependance2.service.d.endpoint}")
    private String serviceDEndpoint;
    @Value("${dependance2.service.e.endpoint}")
    private String serviceEEndpoint;
    @Value("${dependance2.service.f.endpoint}")
    private String serviceFEndpoint;

    @Autowired
    private HealthAggregator healthAggregator;


    @Bean(name = "Dependance2")
    HealthIndicator Dependance2Services() {

        DefaultHealthIndicatorRegistry registry = new DefaultHealthIndicatorRegistry(new LinkedHashMap<>());

        registry.register("ServiceD", this::serviceDHealthCheck);
        registry.register("ServiceE", this::serviceEHealthCheck);
        registry.register("ServiceF", this::serviceFHealthCheck);

        return new CompositeHealthIndicator(healthAggregator, registry);
    }

    private Health serviceDHealthCheck() {
        return healthOf(serviceDEndpoint);
    }    
    
    private Health serviceEHealthCheck() {
        return healthOf(serviceEEndpoint);
    }    
    
    private Health serviceFHealthCheck() {
        return healthOf(serviceFEndpoint);
    }

En valorisant la propriété (name) du bean : @Bean(name = “Dependance2”)

L’url sera donc : http://mon.host.com/mon_application/actuator/health/Dependance2

Et sur appel de cette ressource nous aurons comme résultat :

Dependance2

{
  "status" : "UP",
  "details" : {
    "ServiceD" : {
      "status" : "UP",
      "details" : {
        "URL" : "dependance2.service.d.endpoint
        "Request" : {
          "property1" : "value1",
          "property2" : "value2"
        }
      }
    },
    "ServiceE" : {
      "status" : "UP",
      "details" : {
        "URL" : "dependance2.service.e.endpoint
        "Request" : {
          "property1" : "value1",
          "property2" : "value2"
        }
      }
    },
    "ServiceF" : {
      "status" : "UP",
      "details" : {
        "URL" : "dependance2.service.f.endpoint
        "Request" : {
          "property1" : "value1",
          "property2" : "value2"
        }
      }
    }
  }
}

Conclusion

Dans cet article nous avons utilisé la librairie Spring Boot Actuator pour fournir un bilan de santé applicatif. Nous avons montré, avec cette librairie, comment contrôler nos dépendances externes vitales pour le bon fonctionnement de l’application. Bien entendu, Actuator apporte plus de fonctionnalités permettant la surveillance de nos applications, notamment, la collecte de métriques, la compréhension du trafic et de l’état de la base de données. Pour plus de détails veuillez consulter mon article Spring Boot : outils et astuces.