Intégration Jaeger avec lapplication Spring Boot

(Himank Batra) (9 septembre , 2020)

Jaeger Intégration avec lapplication Spring Boot

Voyons dabord ce quest Jaeger

Jaeger est un logiciel open source pour le traçage des transactions entre les services distribués.

Il est utilisé pour surveiller et dépanner des environnements de microservices complexes.

La société de covoiturage Uber a développé Jaeger en tant que projet open source en 2015. Il a été accepté en tant que Cloud Projet dincubation de la Native Computing Foundation (CNCF) en 2017 et promu au statut de diplômé en 2019.

Quest-ce que le traçage distribué?

Le traçage distribué est un moyen de voir et de comprendre Toute la chaîne dévénements dans une interaction complexe entre microservices.

Le développement logiciel moderne et natif du cloud repose sur des microservices: des services indépendants qui fournissent chacun une fonction de base différente. Lorsquun utilisateur fait une demande dans une application, de nombreux services individuels répondent pour produire un résultat.

Un seul appel dans une application peut appeler des dizaines de services différents qui interagissent les uns avec les autres. Comment les développeurs et les ingénieurs peuvent-ils isoler un problème lorsque quelque chose ne va pas ou quune demande est lente? Nous avons besoin dun moyen de garder une trace de toutes les connexions.

Cest là quintervient le traçage distribué. Il est souvent exécuté dans le cadre dun maillage de services, ce qui est un moyen de gérer et dobserver les microservices.

Jaeger utilise le traçage distribué pour suivre le chemin dune requête via différents microservices. Plutôt que de deviner, nous pouvons voir une représentation visuelle des flux dappels.

Les informations organisées sur les transactions sont utiles pour le débogage et loptimisation. Jaeger comprend des outils pour surveiller les transactions distribuées, optimiser les performances et la latence, et effectuer une analyse des causes profondes (RCA), une méthode de résolution de problèmes.

Terminologie Jaeger et les composants

Jaeger présente les requêtes dexécution sous forme de traces . Une trace montre le chemin des données / exécution à travers un système.

Une trace est composée dun ou plusieurs intervalles . Une travée est une unité logique de travail chez Jaeger. Chaque intervalle comprend le nom de lopération, lheure de début et la durée. Les travées peuvent être imbriquées et ordonnées.

Jaeger comprend plusieurs composants qui fonctionnent ensemble pour collecter, stocker et visualiser les étendues et les traces.

Jaeger Client inclut la langue -implémentations spécifiques de lAPI OpenTracing pour le traçage distribué. Ceux-ci peuvent être utilisés manuellement ou avec une variété de frameworks open-source.

Jaeger Agent est un démon réseau qui écoute les intervalles envoyés via le protocole de datagramme utilisateur. Lagent est destiné à être placé sur le même hôte que lapplication instrumentée. Ceci est généralement implémenté via un side-car dans des environnements de conteneurs comme Kubernetes.

Jaeger Collector reçoit des délais et les place dans une file dattente pour traitement.

Les collecteurs ont besoin dun backend de stockage persistant, Jaeger dispose donc également dun mécanisme enfichable pour span storage .

Query est un service qui récupère les traces du stockage.

Jaeger Console est une interface utilisateur qui vous permet de visualiser vos données de traçage distribuées.

Pourquoi Jaeger?

Comme les praticiens des microservices sur le terrain le réalisent rapidement, la majorité des problèmes opérationnels qui surviennent lors du passage à une architecture distribuée sont finalement liés à deux domaines: la mise en réseau et observabilité. Mettre en réseau et déboguer un ensemble de services distribués entrelacés par rapport à une seule application monolithique est tout simplement un problème dun ordre de grandeur plus important.

Jaeger en action

Nous allons intégrer jaeger sur une application de démarrage à ressort.

Tout dabord, configurons rapidement nos applications de démarrage à ressort.

Lidée ici est de générer des noms en concaténant des noms de scientifiques célèbres avec des noms danimaux.

Nous allons donc construire 3 microservices en utilisant spring boot ie animal-name-service , name-generator-service, et scientist-name-service.

Client Demande dun nom concaténé scientifique et animal de name-generator-service qui appelle en interne animal-name-service et scientist-name-service.

La même chose est illustrée dans le diagramme ci-dessous.

Exemple de microservice

Créons rapidement nos trois microservices en utilisant spring initialize r.

Nous allons ajouter la dépendance spring-boot-starter-web lors de la génération des applications Spring Boot.

Nous avons maintenant 3 applications Spring Boot prêtes. Ajoutons ces 3 microservices à un dossier nommé opentracing-microservices-example.

Et importons ce dossier dans votre éditeur préféré. Jutilise IntelliJ .

Comme nous devons appeler animal-name-service et scientist- name-service de name-generator-service.

nous choisissons de feindre le client pour cela. Ajoutons donc la dépendance spring-cloud-starter-openfeign: 2.2.3.RELEASE dans name-generator-service.

Voici le code pour les 3 microservices.

AnimalNameService :

package com.example.ans;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.core.io.ClassPathResource;import org.springframework.http.HttpHeaders;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestHeader;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.util.List;import java.util.Random;import java.util.stream.Collectors;@SpringBootApplication
public class AnimalNameService {public static void main(String[] args) {SpringApplication.run(AnimalNameService.class, args);}}@RestController
@RequestMapping("/api/v1/animals")
class AnimalNameResource {private final List animalNames;private Random random;public AnimalNameResource() throws IOException {InputStream inputStream = new ClassPathResource("/animals.txt").getInputStream();try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {animalNames = reader.lines().collect(Collectors.toList());}random = new Random();}@GetMapping(path = "/random")
public String name(@RequestHeader HttpHeaders headers) {String name = animalNames.get(random.nextInt(animalNames.size()));return name;}}

application.properties:

server.port=9000

NameGeneratorService:

Ici, jutilise com.shekhargulati: strman: 0.4 .0 bibliothèque pour convertir le nom des animaux et des scientifiques en cas de kebab.

package com.example.ngs;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.openfeign.EnableFeignClients;import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import static strman.Strman.toKebabCase;@SpringBootApplication
@EnableFeignClients
public class NameGeneratorService {public static void main(String[] args) {SpringApplication.run(NameGeneratorService.class, args);}}@FeignClient(name = "scientist-service-client", url = "${scientist.service.prefix.url}")
interface ScientistServiceClient {@GetMapping("/api/v1/scientists/random")
String randomScientistName();}@FeignClient(name = "animal-service-client", url = "${animal.service.prefix.url}")
interface AnimalServiceClient {@GetMapping("/api/v1/animals/random")
String randomAnimalName();}@RestController
@RequestMapping("/api/v1/names")
class NameResource {@Autowired
private AnimalServiceClient animalServiceClient;@Autowired
private ScientistServiceClient scientistServiceClient;@GetMapping(path = "/random")
public String name() throws Exception {String animal = animalServiceClient.randomAnimalName();String scientist = scientistServiceClient.randomScientistName();String name = toKebabCase(scientist) + "-" + toKebabCase(animal);return name;}}

application.properties:

server.port=8080scientist.service.prefix.url=http://localhost:8090animal.service.prefix.url=http://localhost:9000

ScientistNameService:

package com.example.sns;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.core.io.ClassPathResource;import org.springframework.http.HttpHeaders;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestHeader;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.util.List;import java.util.Random;import java.util.stream.Collectors;@SpringBootApplication
public class ScientistNameService {public static void main(String[] args) {SpringApplication.run(ScientistNameService.class, args);}}@RestController
@RequestMapping("/api/v1/scientists")
class ScientistNameResource {private final List scientistsNames;private Random random;public ScientistNameResource() throws IOException {InputStream inputStream = new ClassPathResource("/scientists.txt").getInputStream();try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {scientistsNames = reader.lines().collect(Collectors.toList());}random = new Random();}@GetMapping(path = "/random")
public String name(@RequestHeader HttpHeaders headers) {String name = scientistsNames.get(random.nextInt(scientistsNames.size()));return name;}}

application.properties :

server.port=8090

Nous allons maintenant exécuter les 3 applications et accéder à http: // localhost: 8080 / api / v1 / names / random dans un navigateur.

Nous obtiendrons un exemple de nom aléatoire: john-cockcroft-snapping-turtle

Alors maintenant, notre application la configuration est terminée.

Maintenant, intégrons jaeger à ces applications afin que nous puissions tra ce chaque requête.

Il nous suffit dajouter la dépendance ci-dessous aux 3 pom.xml.


io.opentracing.contrib
opentracing-spring-jaeger-cloud-starter3.1.2

Et nous devons ajouter les propriétés ci-dessous dans le fichier application.properties pour les 3 applications.

spring.application.name= // example : name-generator-service (this will be displayed in jaeger for respective service)opentracing.jaeger.udp-sender.host=localhost //udp host for sender. By default Jaeger libraries use a UDP sender to report finished spans to the jaeger-agent daemonopentracing.jaeger.udp-sender.port=6831 // udp portopentracing.jaeger.log-spans=true // logs the spans in console

Exécutez Jaeger dans le docker via la commande ci-dessous:

docker run -p 9090:16686 — name jaeger -d jaegertracing/all-in-one:1.17

Redémarrez maintenant lapplication. Et accédez à localhost: 9090 dans un navigateur. Nous obtiendrons une page daccueil jaeger.

Aussi, accédez à http: // localhost: 8080 / api / v1 / names / random dans un navigateur. De même, vous obtiendrez un nom aléatoire.

Mais maintenant nous pouvons tracer la requête. Enregistrez-vous dans le tableau de bord jaeger, choisissez le service name-generator-service.

puis cliquez sur rechercher des traces. nous obtiendrons des traces comme indiqué dans limage ci-dessous pour le service-générateur de noms .

Service Jaeger Name Generator

Nous pouvons clairement voir quil y a 5 travées lorsque nous explorons ce sujet.

service-générateur-de-nom (1 étendue)

service-générateur-de-nom-> service-nom-danimal (2 travées)

service-générateur-de-nom-> scientifique- name-service (2 travées)

Ceci est montré dans limage ci-dessous.

Jaeger Name Generator Service Trace

Ici, tout est configuré automatiquement par opentracing-spring-jaeger-cloud-starter bibliothèque qui a une classe nommée TracingAspect faisant essentiellement de la magie.

@Aspect
class TracingAspect {
TracingAspect() {
}

@Around("execution (* feign.Client.*(..)) && !within(is(FinalType))")
public Object feignClientWasCalled(ProceedingJoinPoint pjp) throws Throwable {
Object bean = pjp.getTarget();
if (!(bean instanceof TracingClient)) {
Object[] args = pjp.getArgs();
return (new TracingClientBuilder((Client)bean, FeignTracingAutoConfiguration.this.tracer)).withFeignSpanDecorators(FeignTracingAutoConfiguration.this.spanDecorators).build().execute((Request)args[0], (Options)args[1]);
} else {
return pjp.proceed();
}
}
}

Jai ajouté Dockerfile, docker-compose et docker- setup.sh pour faciliter lexécution de cette application. checkout code et exécutez directement docker-setup.sh.

Vous pouvez trouver le code dans mon dépôt Github link .

Ceci est inspiré du Blog de Shekhar Gulati .

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *