Servicetracing, OpenTracing en Jaeger

Servicetracing, OpenTracing en Jaeger

In onze projecten maken wij gebruik van microservice architectuur. Wanneer zich prestatieknelpunten voordoen, wordt er veel tijd besteed aan het monitoren en parseren van logbestanden. Wanneer u de timing van individuele bewerkingen in een logbestand registreert, is het meestal moeilijk te begrijpen wat tot het oproepen van deze bewerkingen heeft geleid, om de volgorde van acties of de tijdverschuiving van de ene bewerking ten opzichte van de andere in verschillende services bij te houden.

Om handmatige arbeid tot een minimum te beperken, hebben we besloten een van de traceertools te gebruiken. Hoe en waarvoor je tracering kunt gebruiken en hoe wij dat hebben gedaan, wordt in dit artikel besproken.

Welke problemen kunnen worden opgelost met behulp van tracering?

  1. Vind prestatieknelpunten zowel binnen één dienst als in de gehele uitvoeringsboom tussen alle deelnemende diensten. Bijvoorbeeld:
    • Veel korte opeenvolgende oproepen tussen services, bijvoorbeeld naar geocodering of naar een database.
    • Lange I/O-wachttijden, zoals het overbrengen van gegevens via een netwerk of het lezen van schijf.
    • Lange gegevensparsering.
    • Lange bewerkingen waarvoor CPU nodig is.
    • Codedelen die niet nodig zijn om het eindresultaat te krijgen en die kunnen worden verwijderd of vertraagd.
  2. Begrijp duidelijk in welke volgorde wat wordt genoemd en wat er gebeurt als de operatie wordt uitgevoerd.
    Servicetracing, OpenTracing en Jaeger
    Het is te zien dat het verzoek bijvoorbeeld bij de WS-service terechtkwam -> de WS-service vulde de gegevens aan via de R-service -> stuurde het verzoek vervolgens naar de V-service -> de V-service downloadde veel gegevens van de R-service -> ging naar de P-service -> de P-service ging opnieuw naar service R -> service V negeerde het resultaat en ging naar service J -> en stuurde pas daarna het antwoord terug naar service WS, terwijl hij doorging met het berekenen van iets anders in de achtergrond.
    Zonder een dergelijk spoor of gedetailleerde documentatie voor het hele proces is het erg moeilijk om te begrijpen wat er gebeurt als je voor de eerste keer naar de code kijkt, en de code is verspreid over verschillende services en verborgen achter een aantal bakken en interfaces.
  3. Verzameling van informatie over de uitvoeringsboom voor daaropvolgende uitgestelde analyse. In elke uitvoeringsfase kunt u informatie die in deze fase beschikbaar is aan de trace toevoegen en vervolgens uitzoeken welke invoergegevens tot een dergelijk scenario hebben geleid. Bijvoorbeeld:
    • gebruikersnaam
    • rechten
    • Type geselecteerde methode
    • Log- of uitvoeringsfout
  4. Traceringen omzetten in een subset van metrieken en verdere analyse in de vorm van metrieken.

Welke tracering kan worden geregistreerd. Span

Bij het traceren is er het concept van span, dit is een analoog van één log naar de console. Span heeft:

  • Naam, meestal de naam van de methode die is uitgevoerd
  • De naam van de service waarin de reeks is gegenereerd
  • Eigen unieke ID
  • Sommige meta-informatie in de vorm van sleutel/waarde die erin is opgenomen. Bijvoorbeeld methodeparameters en of de methode al dan niet met een fout is geëindigd
  • Begin- en eindtijden voor deze periode
  • Bovenliggende bereik-ID

Elke span wordt naar de spancollector gestuurd om in de database te worden opgeslagen, zodat deze later kan worden bekeken zodra de uitvoering ervan is voltooid. In de toekomst kunt u een boomstructuur van alle reeksen bouwen door ze met elkaar te verbinden op bovenliggende ID. Bij het analyseren kunt u bijvoorbeeld alle perioden in een dienst vinden die meer dan enige tijd in beslag hebben genomen. Ga vervolgens naar een specifiek bereik en bekijk de hele boom boven en onder dit bereik.

Servicetracing, OpenTracing en Jaeger

Opentrace, Jagger en hoe we het voor onze projecten hebben geïmplementeerd

Er is een gemeenschappelijke standaard Open spoor, dat beschrijft hoe en wat moet worden verzameld, zonder door een spoor gebonden te zijn aan een specifieke implementatie in welke taal dan ook. In Java wordt bijvoorbeeld al het werk met traces uitgevoerd via de gemeenschappelijke Opentrace API, en daaronder kan bijvoorbeeld Jaeger worden verborgen of een lege standaardimplementatie die niets doet.
We gebruiken Jager als implementatie van Opentrace. Het bestaat uit verschillende componenten:

Servicetracing, OpenTracing en Jaeger

  • Jaeger-agent is een lokale agent die doorgaans op elke machine wordt geïnstalleerd en waarbij services op de lokale standaardpoort worden aangemeld. Als er geen agent is, zijn de sporen van alle services op deze machine meestal uitgeschakeld
  • Jaeger-collector - alle agenten sturen de verzamelde sporen ernaar en plaatsen deze in de geselecteerde database
  • Database - hun voorkeur gaat uit naar cassandra, maar we gebruiken elasticsearch, er zijn implementaties voor een aantal andere databases en een in-memory-implementatie die niets op schijf opslaat
  • Jaeger-query is een dienst die naar de database gaat en reeds verzamelde sporen retourneert voor analyse
  • Jaeger-ui is een webinterface voor het zoeken en bekijken van sporen, deze draait in jaeger-query

Servicetracing, OpenTracing en Jaeger

Een aparte component kan een openrace jaeger-implementatie voor specifieke talen worden genoemd, via welke spans naar jaeger-agent worden gestuurd.
Jagger verbinden met Java komt neer op het implementeren van de io.opentracing.Tracer-interface, waarna alle sporen er doorheen naar de echte agent zullen vliegen.

Servicetracing, OpenTracing en Jaeger

Je kunt ook veercomponenten aansluiten opentracing-spring-cloud-starter en implementatie van Jaeger opentracing-spring-jaeger-cloud-starter die automatisch de tracering configureert voor alles wat door deze componenten gaat, bijvoorbeeld http-verzoeken aan controllers, verzoeken aan de database via jdbc, enz.

Traceringen vastleggen in Java

Ergens op het hoogste niveau moet de eerste Overspanning worden aangemaakt, dit kan automatisch gebeuren, bijvoorbeeld door de veercontroller bij het ontvangen van een aanvraag, of handmatig als die er niet is. Het wordt vervolgens verzonden via Scope hieronder. Als een van de onderstaande methoden een bereik wil toevoegen, neemt deze de huidige activeSpan uit Scope, maakt een nieuwe span en zegt dat de ouder activeSpan heeft ontvangen, en maakt de nieuwe span actief. Wanneer externe services worden aangeroepen, wordt de huidige actieve reeks aan hen doorgegeven, en die services creëren nieuwe reeksen die aan deze reeks zijn gekoppeld.
Al het werk gaat via de Tracer-instantie; je kunt het verkrijgen via het DI-mechanisme, of GlobalTracer.get() als een globale variabele als het DI-mechanisme niet werkt. Als de tracer niet is geïnitialiseerd, wordt standaard NoopTracer geretourneerd, wat niets doet.
Verder wordt het huidige bereik verkregen van de tracer via de ScopeManager, wordt er een nieuw bereik gemaakt op basis van het huidige bereik met een binding van het nieuwe bereik, en vervolgens wordt het gemaakte bereik gesloten, waardoor het gemaakte bereik wordt gesloten en het vorige bereik terugkeert naar de actieve toestand. Scope is gebonden aan een thread, dus bij multi-threaded programmeren mag u niet vergeten de actieve spanwijdte naar een andere thread over te dragen, voor verdere activering van de Scope van een andere thread met verwijzing naar deze spanwijdte.

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

Voor programmeren met meerdere threads zijn er ook TracedExecutorService en soortgelijke wrappers die de huidige spanwijdte automatisch doorsturen naar de thread wanneer asynchrone taken worden gestart:

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

Voor externe http-verzoeken is er TraceringHttpClient

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

Problemen waarmee we te maken kregen

  • Beans en DI werken dus niet altijd als de tracer niet in een dienst of onderdeel wordt gebruikt Automatisch bedraad Tracer werkt mogelijk niet en u zult GlobalTracer.get() moeten gebruiken.
  • Annotaties werken niet als het geen component of service is, of als de methode wordt aangeroepen vanuit een aangrenzende methode van dezelfde klasse. U moet voorzichtig zijn om te controleren wat werkt en handmatige tracering gebruiken als @Traced niet werkt. Je kunt ook een extra compiler voor Java-annotaties toevoegen, dan zouden ze overal moeten werken.
  • In de oude spring- en springboot werkt de automatische configuratie van opentraing spring cloud niet vanwege bugs in DI. Als je wilt dat sporen in springcomponenten automatisch werken, kun je dit doen naar analogie met 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
  • Proberen met middelen werkt niet in groovy, je moet try final gebruiken.
  • Elke service moet zijn eigen spring.application.name hebben waaronder traceringen worden geregistreerd. Wat betekent een aparte naam voor de verkoop en de test, om ze samen niet te verstoren.
  • Als u GlobalTracer en tomcat gebruikt, hebben alle services die in deze tomcat draaien één GlobalTracer, zodat ze allemaal dezelfde servicenaam hebben.
  • Wanneer u traceringen aan een methode toevoegt, moet u er zeker van zijn dat deze niet vaak in een lus wordt aangeroepen. U moet één gemeenschappelijke tracering voor alle oproepen toevoegen, die de totale bedrijfstijd registreert. Anders ontstaat er overbelasting.
  • Eenmaal in jaeger-ui deden ze te grote verzoeken voor een groot aantal sporen en omdat ze niet op een antwoord wachtten, deden ze het opnieuw. Als gevolg hiervan begon Jaeger-query veel geheugen op te eten en het elastiek te vertragen. Geholpen door jaeger-query opnieuw te starten

Sporen bemonsteren, opslaan en bekijken

Er zijn drie soorten: sporenbemonstering:

  1. Const die alle sporen verzendt en opslaat.
  2. Probabilistisch waarbij sporen met een bepaalde waarschijnlijkheid worden gefilterd.
  3. Ratelimiting die het aantal sporen per seconde beperkt. U kunt deze instellingen op de client configureren, op de jaeger-agent of op de collector. Nu gebruiken we const 1 in de valuator-stack, omdat er niet veel verzoeken zijn, maar deze wel lang duren. Als dit in de toekomst het systeem onnodig belast, kunt u dit beperken.

Als u cassandra gebruikt, worden sporen standaard slechts twee dagen bewaard. We gebruiken elasticsearch en sporen worden de hele tijd bewaard en niet verwijderd. Voor elke dag wordt een aparte index aangemaakt, bijvoorbeeld jaeger-service-2019-03-04. In de toekomst moet u het automatisch opschonen van oude sporen configureren.

Om de sporen te bekijken, moet u:

  • Selecteer de service waarmee u traceringen wilt filteren, bijvoorbeeld tomcat7-default voor een service die in Tomcat wordt uitgevoerd en geen eigen naam kan hebben.
  • Selecteer vervolgens de bewerking, tijdsperiode en minimale bewerkingstijd, bijvoorbeeld vanaf 10 seconden, om alleen lange uitvoeringen uit te voeren.
    Servicetracing, OpenTracing en Jaeger
  • Ga naar een van de sporen en kijk wat daar langzamer ging.
    Servicetracing, OpenTracing en Jaeger

Als er een verzoek-ID bekend is, kunt u een tracering op basis van deze ID vinden via een tagzoekopdracht, als deze ID tijdens de tracering wordt vastgelegd.

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

Artikelen

Video

Bron: www.habr.com

Voeg een reactie