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
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.
Forstå klart i hvilken rækkefølge, hvad der kaldes, og hvad der sker, når operationen udføres.
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.
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
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.
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:
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
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.
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.
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()
);
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.
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
Probabilistisk som filtrerer spor med en given sandsynlighed.
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.
Gå til et af sporene og se, hvad der bremsede der.
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.