Jaeger-integratie met Spring Boot-applicatie

(Himank Batra) (9 september , 2020)

Jaeger-integratie met spring boot-applicatie

Laten we eerst begrijpen wat Jaeger is

Jaeger is open source software voor het traceren van transacties tussen gedistribueerde services.

Het wordt gebruikt voor het bewaken en oplossen van problemen met complexe microservices-omgevingen.

Ridesharingbedrijf Uber ontwikkelde Jaeger als een open source-project in 2015. Het werd geaccepteerd als een Cloud Native Computing Foundation (CNCF) Incubatieproject in 2017 en gepromoveerd tot gegradueerde status in 2019.

Wat is gedistribueerde tracering?

Gedistribueerde tracering is een manier om de De hele reeks gebeurtenissen in een complexe interactie tussen microservices.

Moderne, cloud-native softwareontwikkeling steunt op microservices: onafhankelijke services die elk een andere kernfunctie bieden. Wanneer een gebruiker een verzoek indient in een app, reageren veel individuele services om een ​​resultaat te produceren.

Een enkele oproep in een app kan tientallen verschillende services oproepen die met elkaar communiceren. Hoe kunnen ontwikkelaars en ingenieurs een probleem isoleren als er iets misgaat of een verzoek traag verloopt? We hebben een manier nodig om alle verbindingen bij te houden.

Daar komt gedistribueerde tracering om de hoek kijken. Het wordt vaak uitgevoerd als onderdeel van een servicemesh, wat een manier is om microservices te beheren en te observeren.

Jaeger gebruikt gedistribueerde tracering om het pad van een verzoek door verschillende microservices te volgen. In plaats van te raden, kunnen we een visuele weergave van de oproepstromen zien.

Georganiseerde informatie over transacties is nuttig voor foutopsporing en optimalisatie. Jaeger bevat tools om gedistribueerde transacties te bewaken, prestaties en latentie te optimaliseren en root cause analysis (RCA) uit te voeren, een methode om problemen op te lossen.

Jaeger-terminologie en componenten

Jaeger presenteert uitvoeringsverzoeken als sporen . Een tracering toont het gegevens- / uitvoeringspad door een systeem.

Een tracering bestaat uit een of meer spans . Een span is een logische werkeenheid in Jaeger. Elke reeks bevat de naam van de bewerking, de starttijd en de duur. Overspanningen kunnen worden genest en geordend.

Jaeger bevat verschillende componenten die samenwerken om overspanningen en sporen te verzamelen, op te slaan en te visualiseren.

Jaeger Client omvat taal -specifieke implementaties van de OpenTracing API voor gedistribueerde tracering. Deze kunnen handmatig worden gebruikt of met een verscheidenheid aan open-source frameworks.

Jaeger Agent is een netwerkdaemon die luistert naar reeksen die via User Datagram Protocol worden verzonden. De agent is bedoeld om op dezelfde host te worden geplaatst als de geïnstrumenteerde applicatie. Dit wordt meestal geïmplementeerd via een zijspan in containeromgevingen zoals Kubernetes.

Jaeger Collector ontvangt spans en plaatst ze in een wachtrij voor verwerking.

Collectors hebben een permanente opslag backend, dus Jaeger heeft ook een pluggable mechanisme voor span storage.

Query is een service die sporen uit de opslag haalt.

Jaeger Console is een gebruikersinterface waarmee u uw gedistribueerde traceergegevens kunt visualiseren.

Waarom Jaeger?

Aangezien microservicebedrijven op de grond zich snel realiseren, zijn de meeste operationele problemen die zich voordoen bij de overgang naar een gedistribueerde architectuur uiteindelijk gebaseerd op twee gebieden: netwerken en waarneembaarheid. Het is gewoon een orden van grootte groter probleem om een ​​reeks onderling verweven gedistribueerde services te netwerken en te debuggen in plaats van een enkele monolithische applicatie.

Jaeger in actie

We zullen jaeger integreren in een spring boot-applicaties.

Laten we eerst snel onze spring boot-applicaties opzetten.

Het idee hier is om namen te genereren door namen van bekende wetenschappers samen te voegen met dierennamen.

We zullen dus 3 microservices bouwen met behulp van spring boot, dwz dier-naam-service , name-generator-service, en scientist-name-service.

Clientverzoek om een ​​aaneengeschakelde naam van wetenschapper en dier van name-generator-service die intern dierennaam-service en wetenschapsnaam-service aanroept .

Hetzelfde wordt gedemonstreerd in het onderstaande diagram.

Microservice-voorbeeld

Laten we snel onze drie microservices bouwen met spring initialize r.

We zullen spring-boot-starter-web afhankelijkheid toevoegen tijdens het genereren van spring-boot-applicaties.

Nu hebben we 3 spring-boot-applicaties klaar. Laten we deze 3 microservices toevoegen aan een map met de naam opentracing-microservices-example.

En deze map importeren in je favoriete editor. Ik gebruik IntelliJ .

Zoals we animal-name-service en scientist- moeten noemen name-service van name-generator-service.

we kiezen ervoor om de klant hiervoor te veinzen. Dus laten we spring-cloud-starter-openfeign: 2.2.3.RELEASE afhankelijkheid toevoegen aan name-generator-service.

Hier is de code voor alle 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:

Hier gebruik ik com.shekhargulati: strman: 0.4 .0 bibliotheek voor het converteren van de naam van een dier en wetenschapper naar een kebabzaak.

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

Nu zullen we alle 3 de applicaties draaien en naar http: // localhost: 8080 / api / v1 / names gaan / random in een browser.

We krijgen een voorbeeld van een willekeurige naam: john-cockcroft-snapping-turtle

Dus nu is onze applicatie de installatie is voltooid.

Laten we nu jaeger in deze applicaties integreren, zodat we kunnen tra ce elk verzoek.

We hoeven alleen de onderstaande afhankelijkheid toe te voegen aan alle 3 pom.xml.


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

En we moeten onderstaande eigenschappen toevoegen aan het application.properties-bestand voor alle drie de applicaties.

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

Voer Jaeger in docker uit met het onderstaande commando:

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

Start nu de applicatie opnieuw. En ga naar localhost: 9090 in een browser. We krijgen een jaeger-startpagina.

Ga ook naar http: // localhost: 8080 / api / v1 / names / random in een browser. Op dezelfde manier krijgt u een willekeurige naam.

Maar nu kunnen we het verzoek traceren. Check in jaeger dashboard kies service naam-generator-service.

klik vervolgens op sporen zoeken. we zullen sporen krijgen zoals weergegeven in de onderstaande afbeelding voor de name-generator-service .

Jaeger Name Generator Service

We kunnen duidelijk zien dat er 5 bereiken zijn als we hier op inzoomen.

name-generator-service (1 span)

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

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

Dit wordt getoond in de onderstaande afbeelding.

Jaeger Name Generator Service Trace

Hier wordt alles automatisch geconfigureerd door opentracing-spring-jaeger-cloud-starter bibliotheek met een klasse met de naam TracingAspect die in feite magie doet.

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

Ik heb Dockerfile, docker-compose en docker- setup.sh om het gemakkelijker te maken om deze applicatie uit te voeren. check-out code en start docker-setup.sh rechtstreeks.

Je kunt de code vinden in mijn Github-repository link .

Dit is geïnspireerd door Shekhar Gulatis blog .

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *