Service Tracing, OpenTracing och Jaeger

Service Tracing, OpenTracing och Jaeger

Vi använder mikrotjänstarkitektur i våra projekt. När prestandaflaskhalsar uppstår ägnas mycket tid åt att övervaka och analysera loggar. När man loggar tidpunkterna för enskilda operationer till en loggfil är det vanligtvis svårt att förstå vad som ledde till anropet av dessa operationer, att spåra sekvensen av åtgärder eller tidsförskjutningen av en operation i förhållande till en annan i olika tjänster.

För att minimera manuellt arbete bestämde vi oss för att använda ett av spårningsverktygen. Om hur och varför du kan använda spårning och hur vi gjorde det, och kommer att diskuteras i den här artikeln.

Vilka problem kan lösas med spårning

  1. Hitta prestandaflaskhalsar både inom en enskild tjänst och i hela exekveringsträdet mellan alla deltagande tjänster. Till exempel:
    • Många korta på varandra följande samtal mellan tjänster, till exempel till geokodning eller till en databas.
    • Långa I/O-väntningar, som nätverksöverföringar eller diskläsning.
    • Lång dataanalys.
    • Långa operationer som kräver cpu.
    • Kodavsnitt som inte behövs för att få det slutliga resultatet och som kan tas bort eller försenas.
  2. Förstå tydligt i vilken ordning vad som kallas och vad som händer när operationen utförs.
    Service Tracing, OpenTracing och Jaeger
    Det kan ses att till exempel Request kom till WS-tjänsten -> WS-tjänsten lade till data genom R-tjänsten -> skickade sedan en förfrågan till V-tjänsten -> V-tjänsten laddade mycket data från R-tjänsten tjänst -> gick till P-tjänsten -> P-tjänsten gick igen till tjänst R -> tjänst V ignorerade resultatet och gick till tjänst J -> och returnerade först då svaret till tjänst WS, samtidigt som man fortsatte att beräkna något annat i bakgrund.
    Utan ett sådant spår eller detaljerad dokumentation för hela processen är det väldigt svårt att förstå vad som händer när man tittar på koden för första gången, och koden är spridd över olika tjänster och gömd bakom ett gäng bins och gränssnitt.
  3. Insamling av information om exekveringsträdet för efterföljande uppskjuten analys. I varje skede av exekveringen kan du lägga till information till spåret som är tillgängligt i detta skede och sedan ta reda på vilken indata som ledde till ett liknande scenario. Till exempel:
    • användar ID
    • Rights
    • Typ av vald metod
    • Logg- eller exekveringsfel
  4. Omvandla spår till en delmängd av mått och ytterligare analys redan i form av mått.

Vilket spår kan logga. Spänna

I spårning finns konceptet med ett span, detta är en analog av en stock, till konsolen. Spaet har:

  • Namn, vanligtvis namnet på metoden som kördes
  • Namnet på tjänsten där spann genererades
  • Eget unikt ID
  • Någon sorts metainformation i form av en nyckel/värde som har loggats in i den. Till exempel, metodparametrar eller metoden slutade med ett fel eller inte
  • Start- och sluttider för detta intervall
  • ID för förälderspann

Varje span skickas till span-samlaren för att lagras i databasen för senare granskning så snart den har slutfört sin exekvering. I framtiden kan du bygga ett träd av alla spann genom att ansluta med föräldra-id. När man analyserar kan man till exempel hitta alla spann i någon tjänst som tagit mer än lite tid. Vidare, genom att gå till ett specifikt intervall, se hela trädet ovanför och under detta intervall.

Service Tracing, OpenTracing och Jaeger

Opentrace, Jagger och hur vi implementerade det för våra projekt

Det finns en gemensam standard opentrace, som beskriver hur och vad som ska samlas in, utan att vara knuten till en specifik implementering på något språk. Till exempel i Java utförs allt arbete med spår genom det gemensamma Opentrace API, och under det kan till exempel Jaeger eller en tom standardimplementation som inte gör något döljas.
Vi använder Jaeger som en implementering av Opentrace. Den består av flera komponenter:

Service Tracing, OpenTracing och Jaeger

  • Jaeger-agent är en lokal agent som vanligtvis är installerad på varje maskin och tjänster loggas in på den på den lokala standardporten. Om det inte finns någon agent, är spår av alla tjänster på den här maskinen vanligtvis inaktiverade
  • Jaeger-collector - alla agenter skickar insamlade spår till den, och den placerar dem i den valda databasen
  • Databasen är deras föredragna cassandra, men vi använder elasticsearch, det finns implementeringar för ett par andra databaser och en in-memory implementering som inte sparar något på disken
  • Jaeger-query är en tjänst som går till databasen och returnerar redan insamlade spår för analys
  • Jaeger-ui är ett webbgränssnitt för att söka och visa spår, det går till jaeger-query

Service Tracing, OpenTracing och Jaeger

En separat komponent kan kallas implementeringen av opentrace jaeger för specifika språk, genom vilka spann skickas till jaeger-agent.
Ansluter Jagger i Java handlar om att implementera io.opentracing.Tracer-gränssnittet, varefter alla spår genom det kommer att flyga till den verkliga agenten.

Service Tracing, OpenTracing och Jaeger

Även för fjäderkomponenten kan du ansluta opentracing-fjäder-moln-startare och implementering från Jaeger opentracing-spring-jaeger-cloud-starter som automatiskt kommer att konfigurera spårning för allt som passerar genom dessa komponenter, till exempel http-förfrågningar till kontroller, förfrågningar till databasen via jdbc, etc.

Spårar loggning i Java

Någonstans på toppnivån måste det första Spännet skapas, detta kan göras automatiskt, till exempel av fjäderregulatorn när en förfrågan tas emot, eller manuellt om det inte finns någon. Den sänds sedan genom Scope nedan. Om någon av metoderna nedan vill lägga till ett Span, tar det nuvarande activeSpan från Scope, skapar ett nytt Span och säger att dess överordnade är det resulterande activeSpan, och gör det nya Span aktivt. När externa tjänster anropas skickas det aktuella aktiva intervallet till dem, och dessa tjänster skapar nya intervall med hänvisning till detta intervall.
Allt arbete går genom Tracer-instansen, du kan få det genom DI-mekanismen, eller GlobalTracer.get () som en global variabel om DI-mekanismen inte fungerar. Som standard, om spåraren inte har initierats, kommer NoopTracer att returnera vilket inte gör något.
Vidare erhålls det aktuella omfånget från spåraren genom ScopeManager, ett nytt omfång skapas från det nuvarande med en bindning av det nya omfånget, och sedan stängs det skapade omfånget, vilket stänger det skapade omfånget och returnerar det tidigare omfånget till det aktiva tillståndet. Scope är knutet till en tråd, så vid flertrådsprogrammering får du inte glömma att överföra det aktiva spannet till en annan tråd, för ytterligare aktivering av Scope för en annan tråd med hänvisning till detta intervall.

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)) {
                ...
            }
        });
    }
}

För flertrådsprogrammering finns det också TracedExecutorService och liknande omslag som automatiskt vidarebefordrar det aktuella spannet till tråden när asynkrona uppgifter startas:

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

För externa http-förfrågningar finns det SpårningHttpClient

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

Problem vi stötte på

  • Bönor och DI fungerar inte alltid om spårämnet inte används i en tjänst eller komponent, alltså Autowired Tracer kanske inte fungerar och du måste använda GlobalTracer.get().
  • Anteckningar fungerar inte om det inte är en komponent eller tjänst, eller om metoden anropas från en angränsande metod av samma klass. Du måste vara noga med att kontrollera vad som fungerar och använda manuellt spårskapande om @Traced inte fungerar. Du kan även bifoga en extra kompilator för java-kommentarer, då borde de fungera överallt.
  • I den gamla fjäder- och fjäderstöveln fungerar inte opentraing fjädermolnautokonfigurationen på grund av buggar i DI, om du sedan vill att spåren i fjäderkomponenterna ska fungera automatiskt kan du göra 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
  • Prova med resurser fungerar inte i groovy, du måste använda försök äntligen.
  • Varje tjänst måste ha sitt eget spring.application.name under vilket spår kommer att loggas. Vad gör ett separat namn för försäljningen och testet, för att inte störa dem tillsammans.
  • Om du använder GlobalTracer och tomcat, så har alla tjänster som körs i denna tomcat en GlobalTracer, så de kommer alla att ha samma tjänstnamn.
  • När du lägger till spår till en metod måste du vara säker på att den inte anropas många gånger i en loop. Det är nödvändigt att lägga till ett gemensamt spår för alla samtal, vilket garanterar den totala arbetstiden. Annars skapas en överbelastning.
  • Väl i jaeger-ui gjordes för stora förfrågningar om ett stort antal spår, och eftersom de inte väntade på svar gjorde de det igen. Som ett resultat började jaeger-query äta mycket minne och sakta ner elastisk. Hjälpte till genom att starta om jaeger-query

Provtagning, lagring och visning av spår

Det finns tre typer provtagningsspår:

  1. Const som skickar och sparar alla spår.
  2. Probabilistisk som filtrerar spår med viss sannolikhet.
  3. Ratelimiting som begränsar antalet spår per sekund. Du kan konfigurera dessa inställningar på klienten, antingen på jaeger-agenten eller på insamlaren. Nu använder vi const 1 i värderingsstacken, eftersom det inte finns så många förfrågningar, men de tar lång tid. I framtiden, om detta kommer att utöva en överdriven belastning på systemet, kan du begränsa det.

Om du använder cassandra lagrar den som standard bara spår i två dagar. Vi använder elastisk sökning och spår lagras för all framtid och raderas inte. Ett separat index skapas för varje dag, till exempel jaeger-service-2019-03-04. I framtiden måste du konfigurera automatisk rengöring av gamla spår.

För att se spåren behöver du:

  • Välj tjänsten som du vill filtrera spår efter, till exempel tomcat7-default för en tjänst som körs i tomcat och inte kan ha ett eget namn.
  • Välj sedan operation, tidsintervall och minsta drifttid, till exempel från 10 sekunder, för att endast ta långa exekveringar.
    Service Tracing, OpenTracing och Jaeger
  • Gå till ett av spåren och se vad som saktade ner där.
    Service Tracing, OpenTracing och Jaeger

Dessutom, om något förfrågnings-id är känt, kan du hitta ett spår av detta ID genom en taggsökning, om detta ID är loggat i spårningsintervallet.

Документация

Artiklar

Video

Källa: will.com

Lägg en kommentar