Integrare Jaeger cu aplicația Spring Boot

(Himank Batra) (9 sept. , 2020)

Jaeger Integration with spring boot application

Să înțelegem mai întâi ce este Jaeger

Jaeger este software open source pentru urmărirea tranzacțiilor între servicii distribuite.

Este utilizat pentru monitorizarea și depanarea mediilor complexe de microservicii.

Compania de transferuri Uber a dezvoltat Jaeger ca proiect open source în 2015. A fost acceptat ca Cloud Proiect de incubare a Native Computing Foundation (CNCF) în 2017 și promovat la statut de absolvent în 2019.

Ce este urmărirea distribuită?

Urmărirea distribuită este un mod de a vedea și a înțelege Întregul lanț de evenimente într-o interacțiune complexă între microservicii.

Dezvoltarea modernă de software nativă bazată pe cloud se bazează pe microservicii: servicii independente care oferă fiecare o funcție de bază diferită. Când un utilizator face o solicitare într-o aplicație, multe servicii individuale răspund pentru a produce un rezultat.

Un singur apel într-o aplicație poate invoca zeci de servicii diferite care interacționează între ele. Cum pot dezvoltatorii și inginerii să izoleze o problemă atunci când ceva nu merge bine sau o cerere rulează lent? Avem nevoie de o modalitate de a ține evidența tuturor conexiunilor.

Aici intervine urmărirea distribuită. De multe ori se execută ca parte a unei rețele de servicii, care este o modalitate de a gestiona și observa microserviciile.

Jaeger folosește urmărirea distribuită pentru a urma calea unei cereri prin diferite microservicii. În loc să ghicim, putem vedea o reprezentare vizuală a fluxurilor de apeluri.

Informațiile organizate despre tranzacții sunt utile pentru depanare și optimizare. Jaeger include instrumente pentru a monitoriza tranzacțiile distribuite, pentru a optimiza performanța și latența și pentru a efectua analiza cauzei principale (RCA), o metodă de rezolvare a problemelor.

Terminologia Jaeger și componentele

Jaeger prezintă cereri de execuție ca urme . O urmă arată calea de date / execuție printr-un sistem.

O urmă este alcătuită din una sau mai multe întinderi . O durată este o unitate logică de lucru în Jaeger. Fiecare interval include numele operației, ora de începere și durata. Extensiile pot fi imbricate și ordonate.

Jaeger include mai multe componente care funcționează împreună pentru a colecta, stoca și vizualiza întinderi și urme.

Client Jaeger include limbajul -implementări specifice API-ului OpenTracing pentru urmărirea distribuită. Acestea pot fi utilizate manual sau cu o varietate de cadre open-source.

Jaeger Agent este un daemon de rețea care ascultă intervalele trimise prin Protocolul Datagram de utilizator. Agentul este destinat să fie plasat pe aceeași gazdă ca aplicația instrumentată. Acest lucru este de obicei implementat printr-un sidecar în medii de containere precum Kubernetes.

Jaeger Collector primește întinderi și le plasează într-o coadă pentru procesare.

Colectorii necesită o backend de stocare persistent, astfel încât Jaeger are și un mecanism conectabil pentru span stocare.

Interogare este un serviciu care recuperează urmele din stocare.

Jaeger Console este o interfață cu utilizatorul care vă permite să vizualizați datele de urmărire distribuite.

De ce Jaeger?

Pe măsură ce practicienii de microservicii de la sol își dau seama rapid, majoritatea problemelor operaționale care apar atunci când se trece la o arhitectură distribuită se bazează în cele din urmă pe două domenii: rețea și observabilitate. Este pur și simplu o problemă mai mare în ordinea mărimii pentru a rețea și a depana un set de servicii distribuite între ele, comparativ cu o singură aplicație monolitică.

Jaeger in Action

Vom integra jaeger pe aplicații de pornire de primăvară.

Mai întâi, să configurăm rapid aplicațiile noastre de pornire de primăvară.

Ideea de aici generează nume concatenând nume de oameni de știință celebri cu nume de animale.

Deci, vom construi 3 microservicii folosind boot-ul de primăvară, adică animal-name-service , name-generator-service, și scientist-name-service.

Solicitare client pentru un om de știință și nume concatenat de animale de la name-generator-service care apelează intern animal-name-service și scientient-name-service.

Același lucru este demonstrat în diagrama de mai jos.

Exemplu de microserviciu

Să construim rapid cele trei microservicii folosind spring initialize r.

Vom adăuga dependența spring-boot-starter-web în timp ce generăm aplicații de boot de primăvară.

Acum avem gata 3 aplicații de boot de primăvară. Să adăugăm aceste 3 microservicii într-un folder numit opentracing-microservices-example.

Și să importăm acest folder în editorul dvs. preferat. Folosesc IntelliJ .

Așa cum trebuie să numim animal-name-service și scientist- name-service de la name-generator-service.

alegem să simulăm clientul în acest sens. Deci, să adăugăm spring-cloud-starter-openfeign: 2.2.3.RELEASE dependență în nume-generator-serviciu.

Iată codul pentru toate cele 3 microservicii.

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:

Aici, folosesc com.shekhargulati: strman: 0.4 .0 bibliotecă pentru conversia numelui animalului și al omului de știință în cazul 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

Acum vom rula toate cele 3 aplicații și vom accesa http: // localhost: 8080 / api / v1 / names / random într-un browser.

Vom primi un exemplu de nume aleatoriu: john-cockcroft-snapping-turtle

Deci, acum aplicația noastră configurarea este terminată.

Acum hai să integrăm jaeger în aceste aplicații, astfel încât să putem trece pentru fiecare cerere.

Trebuie doar să adăugăm dependența de mai jos la toate cele 3 pom.xml.


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

Și trebuie să adăugăm mai jos proprietăți în fișierul application.properties pentru toate cele 3 aplicații.

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

Rulați Jaeger în docker prin comanda de mai jos:

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

Acum reporniți aplicația. Și accesați localhost: 9090 într-un browser. Vom primi o pagină de pornire jaeger.

De asemenea, accesați http: // localhost: 8080 / api / v1 / names / random într-un browser. În mod similar, veți obține un nume aleatoriu.

Dar acum putem urmări cererea. Verificați tabloul de bord Jaeger, alegeți serviciul nume-generator-serviciu.

apoi faceți clic pe Găsiți urmele. vom obține urme așa cum se arată în imaginea de mai jos pentru name-generator-service .

Jaeger Name Generator Service

Putem vedea clar că există 5 întinderi când analizăm acest lucru.

name-generator-service (1 span)

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

name-generator-service-> scientist- service-nume (2 întinderi)

Aceasta este afișată în imaginea de mai jos.

Jaeger Name Generator Service Trace

Aici, totul este configurat automat de opentracing-spring-jaeger-cloud-starter bibliotecă care are o clasă numită TracingAspect practic care face 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();
}
}
}

Am adăugat Dockerfile, docker-compose și docker- setup.sh pentru a face mai ușoară rularea acestei aplicații. verificați codul și rulați direct docker-setup.sh direct.

Puteți găsi codul în depozitul meu Github link .

Aceasta este inspirată de Blogul lui Shekhar Gulati .

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *