Integrazione di Jaeger con lapplicazione Spring Boot

(Himank Batra) (9 settembre , 2020)

Integrazione Jaeger con lapplicazione di avvio a molla

Prima di tutto capiamo cosè Jaeger

Jaeger è un software open source per il tracciamento delle transazioni tra servizi distribuiti.

Viene utilizzato per il monitoraggio e la risoluzione dei problemi di ambienti di microservizi complessi.

Lazienda di Ridesharing Uber ha sviluppato Jaeger come progetto open source nel 2015. È stato accettato come Cloud Progetto di incubazione della Native Computing Foundation (CNCF) nel 2017 e promosso allo stato di laurea nel 2019.

Che cosè il tracciamento distribuito?

La traccia distribuita è un modo per vedere e capire Lintera catena di eventi in una complessa interazione tra microservizi.

Lo sviluppo di software moderno e cloud-native si basa sui microservizi: servizi indipendenti che forniscono ciascuno una diversa funzione principale. Quando un utente effettua una richiesta in unapp, molti servizi individuali rispondono per produrre un risultato.

Una singola chiamata in unapp può richiamare dozzine di servizi diversi che interagiscono tra loro. In che modo sviluppatori e ingegneri possono isolare un problema quando qualcosa va storto o una richiesta viene eseguita lentamente? Abbiamo bisogno di un modo per tenere traccia di tutte le connessioni.

È qui che entra in gioco la traccia distribuita. Spesso viene eseguita come parte di una mesh di servizi, che è un modo per gestire e osservare i microservizi.

Jaeger utilizza la traccia distribuita per seguire il percorso di una richiesta attraverso diversi microservizi. Piuttosto che indovinare, possiamo vedere una rappresentazione visiva dei flussi di chiamate.

Le informazioni organizzate sulle transazioni sono utili per il debug e lottimizzazione. Jaeger include strumenti per monitorare le transazioni distribuite, ottimizzare le prestazioni e la latenza ed eseguire lanalisi delle cause alla radice (RCA), un metodo di risoluzione dei problemi.

Terminologia Jaeger e componenti

Jaeger presenta le richieste di esecuzione come tracce . Una traccia mostra i dati / percorso di esecuzione attraverso un sistema.

Una traccia è composta da uno o più intervalli . Uno span è ununità logica di lavoro in Jaeger. Ogni intervallo include il nome delloperazione, lora di inizio e la durata. Gli intervalli possono essere annidati e ordinati.

Jaeger include diversi componenti che lavorano insieme per raccogliere, memorizzare e visualizzare intervalli e tracce.

Jaeger Client include la lingua -implementazioni specifiche dellAPI OpenTracing per la traccia distribuita. Questi possono essere usati manualmente o con una varietà di framework open source.

Jaeger Agent è un demone di rete che ascolta gli intervalli inviati tramite il protocollo datagramma utente. Lagente deve essere posizionato sullo stesso host dellapplicazione strumentata. Questo di solito viene implementato tramite un sidecar in ambienti container come Kubernetes.

Jaeger Collector riceve gli intervalli e li mette in una coda per lelaborazione.

I servizi di raccolta richiedono un backend di archiviazione persistente, quindi Jaeger ha anche un meccanismo collegabile per lo span storage.

Query è un servizio che recupera le tracce dallo storage.

Jaeger Console è uninterfaccia utente che consente di visualizzare i dati di tracciamento distribuiti.

Perché Jaeger?

Come i professionisti di microservizi sul campo si stanno rapidamente rendendo conto, la maggior parte dei problemi operativi che sorgono quando si passa a unarchitettura distribuita sono fondamentalmente radicati in due aree: networking e osservabilità. È semplicemente un problema più grande di un ordine di grandezza mettere in rete ed eseguire il debug di un insieme di servizi distribuiti intrecciati rispetto a una singola applicazione monolitica.

Jaeger in Action

Integreremo jaeger in applicazioni di avvio a molla.

Per prima cosa, configuriamo rapidamente le nostre applicazioni di avvio a molla.

Lidea qui è generare nomi concatenando nomi di scienziati famosi con nomi di animali.

Quindi, costruiremo 3 microservizi usando lo stivale a molla cioè servizio-nome-animale , > name-generator-service, e scientist-name-service.

Richiesta del cliente di uno scienziato e un nome concatenato da un animale da name-generator-service che chiama internamente animal-name-service e scientist-name-service.

Lo stesso è dimostrato nel diagramma sottostante.

Esempio di microservizio

Creiamo rapidamente i nostri tre microservizi utilizzando spring initialize r.

Aggiungeremo la dipendenza spring-boot-starter-web durante la generazione delle applicazioni di avvio a molla.

Ora abbiamo 3 applicazioni di avvio a molla pronte. Aggiungiamo questi 3 microservizi a una cartella denominata opentracing-microservices-example.

E importiamo questa cartella nel tuo editor preferito. Uso IntelliJ .

Poiché dobbiamo chiamare animal-name-service e scientist- name-service da name-generator-service.

stiamo scegliendo di fingere client per questo. Quindi aggiungiamo spring-cloud-starter-openfeign: 2.2.3.RELEASE dipendenza in name-generator-service.

Ecco il codice per tutti e 3 i microservizi.

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:

Qui sto usando com.shekhargulati: strman: 0.4 .0 libreria per convertire il nome dellanimale e dello scienziato in caso di 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

Ora eseguiremo tutte e 3 le applicazioni e andremo a http: // localhost: 8080 / api / v1 / names / random in un browser.

Otterremo un esempio di nome casuale: john-cockcroft-snapping-turtle

Quindi ora la nostra applicazione linstallazione è terminata.

Ora integriamo jaeger in queste applicazioni in modo da poter tra ce ogni richiesta.

Dobbiamo solo aggiungere la seguente dipendenza a tutti e 3 i pom.xml.


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

E dobbiamo aggiungere le seguenti proprietà nel file application.properties per tutte e 3 le applicazioni.

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

Esegui Jaeger nella finestra mobile tramite il comando seguente:

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

Ora riavvia lapplicazione. E vai su localhost: 9090 in un browser. Otterremo una homepage jaeger.

Inoltre, vai a http: // localhost: 8080 / api / v1 / names / random in un browser. Allo stesso modo, otterrai un nome casuale.

Ma ora possiamo tracciare la richiesta. Controlla nella dashboard jaeger scegli il servizio name-generator-service.

quindi fai clic su trova tracce. otterremo le tracce come mostrato nellimmagine sottostante per il servizio-generatore di nomi .

Servizio Jaeger Name Generator

Possiamo vedere chiaramente che ci sono 5 intervalli quando analizziamo questo aspetto.

name-generator-service (1 span)

name-generator-service-> animal-name-service (2 span)

name-generator-service-> scientist- name-service (2 intervalli)

Questo è mostrato nellimmagine sotto.

Traccia del servizio generatore di nomi Jaeger

Qui, tutto è configurato automaticamente da opentracing-spring-jaeger-cloud-starter libreria che ha una classe denominata TracingAspect fondamentalmente che fa magia.

@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();
}
}
}

Ho aggiunto Dockerfile, docker-compose e docker- setup.sh per rendere più semplice lesecuzione di questa applicazione. controlla il codice ed esegui docker-setup.sh direttamente.

Puoi trovare il codice nel mio repository Github link .

Questo è ispirato al Blog di Shekhar Gulati .

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *