Service Tracing, OpenTracing og Jaeger

Service Tracing, OpenTracing og Jaeger

Vi bruger mikroservicearkitektur i vores projekter. Når der opstår flaskehalse i ydeevnen, bruges der meget tid på at overvåge og analysere logfiler. Når du logger timingen af ​​individuelle operationer til en logfil, er det normalt vanskeligt at forstå, hvad der førte til påkaldelsen af ​​disse operationer, at spore rækkefølgen af ​​handlinger eller tidsforskydningen af ​​en operation i forhold til en anden i forskellige tjenester.

For at minimere manuelt arbejde besluttede vi at bruge et af sporingsværktøjerne. Om hvordan og hvorfor du kan bruge sporing og hvordan vi gjorde det, og vil blive diskuteret i denne artikel.

Hvilke problemer kan løses med sporing

  1. Find ydeevneflaskehalse både inden for en enkelt tjeneste og i hele udførelsestræet mellem alle deltagende tjenester. For eksempel:
    • Mange korte på hinanden følgende opkald mellem tjenester, for eksempel til geokodning eller til en database.
    • Lange I/O-venter, såsom netværksoverførsler eller disklæsninger.
    • Lang dataparsing.
    • Lange operationer, der kræver cpu.
    • Sektioner af kode, der ikke er nødvendige for at få det endelige resultat og kan fjernes eller forsinkes.
  2. Forstå klart i hvilken rækkefølge, hvad der kaldes, og hvad der sker, når operationen udføres.
    Service Tracing, OpenTracing og Jaeger
    Det kan ses, at for eksempel Request kom til WS-tjenesten -> WS-tjenesten supplerede data gennem R-tjenesten -> sendte derefter en anmodning til V-tjenesten -> V-tjenesten indlæste en masse data fra R service -> gik til P-service -> P-service gik igen til service R -> service V ignorerede resultatet og gik til service J -> og returnerede først derefter svaret til service WS, mens man fortsatte med at beregne noget andet i baggrunden.
    Uden et sådant spor eller detaljeret dokumentation for hele processen, er det meget svært at forstå, hvad der sker, når man ser på koden for første gang, og koden er spredt ud over forskellige tjenester og gemt bag en masse skraldespande og grænseflader.
  3. Indsamling af information om udførelsestræet til efterfølgende udskudt analyse. På hvert trin i udførelsen kan du tilføje information til sporet, der er tilgængeligt på dette trin, og derefter finde ud af, hvilke inputdata der førte til et lignende scenario. For eksempel:
    • bruger ID
    • rettigheder
    • Type valgt metode
    • Log eller udførelsesfejl
  4. At omdanne spor til en delmængde af metrics og yderligere analyse allerede i form af metrics.

Hvilket spor kan logge. Spændvidde

I sporing er der konceptet med et spænd, dette er en analog af en log til konsollen. Spaen har:

  • Navn, normalt navnet på den metode, der blev udført
  • Navnet på den tjeneste, hvor spændet blev genereret
  • Eget unikt ID
  • En slags metainformation i form af en nøgle/værdi, der er blevet logget ind på den. For eksempel, metodeparametre eller metoden endte med en fejl eller ej
  • Start- og sluttider for dette spænd
  • Forældrespan-id

Hvert span sendes til span-samleren for at blive gemt i databasen til senere gennemgang, så snart det har afsluttet sin udførelse. I fremtiden kan du bygge et træ med alle spændvidder ved at forbinde med forældre-id. Når du analyserer, kan du for eksempel finde alle spændene i en tjeneste, der tog mere end noget tid. Yderligere, ved at gå til et specifikt spænd, se hele træet over og under dette spænd.

Service Tracing, OpenTracing og Jaeger

Opentrace, Jagger og hvordan vi implementerede det til vores projekter

Der er en fælles standard opentrace, som beskriver hvordan og hvad der skal indsamles, uden at være bundet ved at spore til en specifik implementering på noget sprog. For eksempel i Java udføres alt arbejde med spor gennem det fælles Opentrace API, og under det kan for eksempel Jaeger eller en tom standardimplementering, der ikke gør noget, skjules.
Vi bruger Jaeger som en implementering af Opentrace. Den består af flere komponenter:

Service Tracing, OpenTracing og Jaeger

  • Jaeger-agent er en lokal agent, der normalt er installeret på hver maskine, og tjenester er logget ind på den på den lokale standardport. Hvis der ikke er nogen agent, er spor af alle tjenester på denne maskine normalt deaktiveret
  • Jaeger-collector - alle agenter sender indsamlede spor til den, og den placerer dem i den valgte database
  • Databasen er deres foretrukne cassandra, men vi bruger elasticsearch, der er implementeringer til et par andre databaser og en in-memory implementering, der ikke gemmer noget på disken
  • Jaeger-query er en tjeneste, der går til databasen og returnerer allerede indsamlede spor til analyse
  • Jaeger-ui er en webgrænseflade til søgning og visning af spor, den går til jaeger-query

Service Tracing, OpenTracing og Jaeger

En separat komponent kan kaldes implementeringen af ​​opentrace jaeger for specifikke sprog, hvorigennem spænd sendes til jaeger-agent.
Tilslutning af Jagger i Java kommer ned til at implementere io.opentracing.Tracer-grænsefladen, hvorefter alle spor gennem den vil flyve til den rigtige agent.

Service Tracing, OpenTracing og Jaeger

Også til fjederkomponenten kan du tilslutte opentracing-spring-sky-starter og implementering fra Jaeger opentracing-spring-jaeger-cloud-starter som automatisk vil konfigurere sporing for alt, der passerer gennem disse komponenter, for eksempel http-anmodninger til controllere, anmodninger til databasen gennem jdbc osv.

Sporer logning i Java

Et eller andet sted på øverste niveau skal det første Span oprettes, dette kan gøres automatisk, for eksempel af fjedercontrolleren når en anmodning modtages, eller manuelt hvis der ikke er nogen. Det transmitteres derefter gennem Scope nedenfor. Hvis nogen af ​​metoderne nedenfor ønsker at tilføje et spænd, tager det det aktuelle activeSpan fra omfanget, opretter et nyt spænd og siger, at dets overordnede er det resulterende activeSpan, og gør det nye spænd aktivt. Når du kalder eksterne tjenester, videregives det aktuelle aktive spænd til dem, og disse tjenester opretter nye spænd med reference til dette spænd.
Alt arbejde går gennem Tracer-instansen, du kan få det gennem DI-mekanismen eller GlobalTracer.get () som en global variabel, hvis DI-mekanismen ikke virker. Som standard, hvis tracer ikke er blevet initialiseret, vender NoopTracer tilbage, hvilket ikke gør noget.
Yderligere hentes det aktuelle scope fra sporeren gennem ScopeManageren, et nyt scope oprettes fra det nuværende med en binding af det nye span, og derefter lukkes det oprettede Scope, hvilket lukker det oprettede span og returnerer det tidligere Scope til den aktive tilstand. Scope er bundet til en tråd, så ved flertrådet programmering må du ikke glemme at overføre det aktive spænd til en anden tråd, for yderligere aktivering af scope for en anden tråd med reference til dette spænd.

io.opentracing.Tracer tracer = ...; // GlobalTracer.get()

void DoSmth () {
   try (Scope scope = tracer.buildSpan("DoSmth").startActive(true)) {
      ...
   }
}
void DoOther () {
    Span span = tracer.buildSpan("someWork").start();
    try (Scope scope = tracer.scopeManager().activate(span, false)) {
        // Do things.
    } catch(Exception ex) {
        Tags.ERROR.set(span, true);
        span.log(Map.of(Fields.EVENT, "error", Fields.ERROR_OBJECT, ex, Fields.MESSAGE, ex.getMessage()));
    } finally {
        span.finish();
    }
}

void DoAsync () {
    try (Scope scope = tracer.buildSpan("ServiceHandlerSpan").startActive(false)) {
        ...
        final Span span = scope.span();
        doAsyncWork(() -> {
            // STEP 2 ABOVE: reactivate the Span in the callback, passing true to
            // startActive() if/when the Span must be finished.
            try (Scope scope = tracer.scopeManager().activate(span, false)) {
                ...
            }
        });
    }
}

Til multi-threaded programmering er der også TracedExecutorService og lignende wrappers, der automatisk videresender det aktuelle span til tråden, når asynkrone opgaver startes:

private ExecutorService executor = new TracedExecutorService(
    Executors.newFixedThreadPool(10), GlobalTracer.get()
);

For eksterne http-anmodninger er der TracingHttpClient

HttpClient httpClient = new TracingHttpClientBuilder().build();

Problemer vi stod over for

  • Bønner og DI virker altså ikke altid, hvis sporstoffet ikke bruges i en service eller komponent Autowired Tracer virker muligvis ikke, og du bliver nødt til at bruge GlobalTracer.get().
  • Annoteringer virker ikke, hvis det ikke er en komponent eller tjeneste, eller hvis metoden kaldes fra en tilstødende metode af samme klasse. Du skal være omhyggelig med at tjekke, hvad der virker, og bruge manuel sporoprettelse, hvis @Traced ikke virker. Du kan også vedhæfte en ekstra compiler til java-annoteringer, så burde de virke overalt.
  • I den gamle fjeder- og fjederstøvle virker opentraing fjedersky autokonfigurationen ikke på grund af fejl i DI, så hvis du vil have sporene i fjederkomponenterne til at virke automatisk, kan du gøre det analogt med github.com/opentracing-contrib/java-spring-jaeger/blob/master/opentracing-spring-jaeger-starter/src/main/java/io/opentracing/contrib/java/spring/jaeger/starter/JaegerAutoConfiguration.java
  • Prøv med ressourcer virker ikke i groovy, du skal bruge prøv endelig.
  • Hver tjeneste skal have sit eget spring.application.name, som spor vil blive logget under. Hvad betyder et separat navn for salg og test, for ikke at forstyrre dem sammen.
  • Hvis du bruger GlobalTracer og tomcat, så har alle tjenester, der kører i denne tomcat, én GlobalTracer, så de vil alle have det samme servicenavn.
  • Når du tilføjer spor til en metode, skal du være sikker på, at den ikke kaldes mange gange i en løkke. Det er nødvendigt at tilføje ét fælles spor for alle opkald, hvilket garanterer den samlede arbejdstid. Ellers vil der blive skabt en overskydende belastning.
  • En gang i jaeger-ui blev der stillet for store anmodninger om et stort antal spor, og da de ikke ventede på svar, gjorde de det igen. Som et resultat begyndte jaeger-query at æde en masse hukommelse og bremse elastikken. Hjælpet ved at genstarte jaeger-query

Prøvetagning, lagring og visning af spor

Der er tre typer prøveudtagningsspor:

  1. Const som sender og gemmer alle spor.
  2. Probabilistisk som filtrerer spor med en given sandsynlighed.
  3. Ratelimiting som begrænser antallet af spor pr. sekund. Du kan konfigurere disse indstillinger på klienten, enten på jaeger-agenten eller på samleren. Nu bruger vi const 1 i valuator stakken, da der ikke er ret mange anmodninger, men de tager lang tid. I fremtiden, hvis dette vil udøve en overdreven belastning på systemet, kan du begrænse det.

Hvis du bruger cassandra, gemmer den som standard kun spor i to dage. Vi bruger elastiksøgning og spor gemmes for altid og slettes ikke. Der oprettes et separat indeks for hver dag, for eksempel jaeger-service-2019-03-04. I fremtiden skal du konfigurere automatisk rensning af gamle spor.

For at se sporene skal du bruge:

  • Vælg den tjeneste, som du vil filtrere spor efter, for eksempel tomcat7-standard for en tjeneste, der kører i tomcat og ikke kan have sit eget navn.
  • Vælg derefter operationen, tidsintervallet og minimum operationstid, for eksempel fra 10 sekunder, for kun at tage lange henrettelser.
    Service Tracing, OpenTracing og Jaeger
  • Gå til et af sporene og se, hvad der bremsede der.
    Service Tracing, OpenTracing og Jaeger

Hvis et anmodnings-id er kendt, kan du også finde et spor efter dette id gennem en tagsøgning, hvis dette id er logget i sporingsområdet.

Records

Artikler

Video

Kilde: www.habr.com

Tilføj en kommentar