Servizio Tracing, OpenTracing e Jaeger

Servizio Tracing, OpenTracing e Jaeger

Utilizziamo l'architettura a microservizi nei nostri progetti. Quando si verificano colli di bottiglia nelle prestazioni, viene dedicato molto tempo al monitoraggio e all'analisi dei registri. Quando si registrano i tempi delle singole operazioni in un file di registro, di solito è difficile capire cosa ha portato all'invocazione di queste operazioni, tenere traccia della sequenza di azioni o dello spostamento temporale di un'operazione rispetto a un'altra in servizi diversi.

Per ridurre al minimo il lavoro manuale, abbiamo deciso di utilizzare uno degli strumenti di tracciamento. Su come e perché puoi usare la traccia e come l'abbiamo fatto, e sarà discusso in questo articolo.

Quali problemi possono essere risolti con il tracciamento

  1. Trova i colli di bottiglia delle prestazioni sia all'interno di un singolo servizio che nell'intero albero di esecuzione tra tutti i servizi partecipanti. Per esempio:
    • Molte brevi chiamate consecutive tra servizi, ad esempio, alla geocodifica o a un database.
    • Lunghe attese di I/O, ad esempio trasferimenti di rete o letture su disco.
    • Lunga analisi dei dati.
    • Operazioni lunghe che richiedono cpu.
    • Sezioni di codice che non sono necessarie per ottenere il risultato finale e possono essere rimosse o ritardate.
  2. Comprendi chiaramente in quale sequenza viene chiamato e cosa succede quando viene eseguita l'operazione.
    Servizio Tracing, OpenTracing e Jaeger
    Si può vedere che, ad esempio, la richiesta è arrivata al servizio WS -> il servizio WS ha integrato i dati tramite il servizio R -> quindi ha inviato una richiesta al servizio V -> il servizio V ha caricato molti dati dal Servizio R -> è andato al servizio P -> il servizio P è andato di nuovo al servizio R -> il servizio V ha ignorato il risultato ed è andato al servizio J -> e solo allora ha restituito la risposta al servizio WS, continuando a calcolare qualcos'altro in lo sfondo.
    Senza una tale traccia o documentazione dettagliata per l'intero processo, è molto difficile capire cosa sta succedendo quando guardi il codice per la prima volta, e il codice è sparso su diversi servizi e nascosto dietro un mucchio di contenitori e interfacce.
  3. Raccolta di informazioni sull'albero di esecuzione per la successiva analisi differita. In ogni fase dell'esecuzione, puoi aggiungere informazioni alla traccia disponibile in questa fase e quindi capire quali dati di input hanno portato a uno scenario simile. Per esempio:
    • ID utente
    • diritti
    • Tipo di metodo selezionato
    • Log o errore di esecuzione
  4. Trasformare le tracce in un sottoinsieme di metriche e ulteriori analisi già sotto forma di metriche.

Quale traccia può registrare. Durata

Nella traccia c'è il concetto di span, questo è un analogo di un registro, alla console. Il centro benessere dispone di:

  • Nome, in genere il nome del metodo che è stato eseguito
  • Il nome del servizio in cui è stato generato lo span
  • proprio ID univoco
  • Una sorta di meta informazione sotto forma di una chiave/valore a cui è stato effettuato l'accesso. Ad esempio, i parametri del metodo o il metodo sono terminati con un errore o meno
  • Orari di inizio e fine per questo intervallo
  • ID intervallo padre

Ogni intervallo viene inviato al raccoglitore di intervallo per essere memorizzato nel database per una successiva revisione non appena ha completato la sua esecuzione. In futuro, puoi creare un albero di tutte le estensioni collegandoti tramite l'ID genitore. Durante l'analisi, puoi trovare, ad esempio, tutti gli intervalli in un servizio che ha richiesto più tempo. Inoltre, andando a un intervallo specifico, vedi l'intero albero sopra e sotto questo intervallo.

Servizio Tracing, OpenTracing e Jaeger

Opentrace, Jagger e come lo abbiamo implementato per i nostri progetti

C'è uno standard comune opentrace, che descrive come e cosa dovrebbe essere raccolto, senza essere vincolato dal tracciamento a un'implementazione specifica in qualsiasi lingua. Ad esempio, in Java, tutto il lavoro con le tracce viene eseguito tramite l'API Opentrace comune e sotto di essa, ad esempio, Jaeger o un'implementazione predefinita vuota che non fa nulla può essere nascosta.
Stiamo usando Jaeger come implementazione di Opentrace. Consiste di diversi componenti:

Servizio Tracing, OpenTracing e Jaeger

  • Jaeger-agent è un agente locale che di solito è installato su ogni macchina e i servizi sono registrati su di esso sulla porta predefinita locale. Se non è presente alcun agente, le tracce di tutti i servizi su questa macchina sono generalmente disabilitate
  • Jaeger-collector: tutti gli agenti inviano le tracce raccolte ad esso e le inserisce nel database selezionato
  • Il database è la loro cassandra preferita, ma usiamo elasticsearch, ci sono implementazioni per un paio di altri database e un'implementazione in memoria che non salva nulla su disco
  • Jaeger-query è un servizio che va al database e restituisce le tracce già raccolte per l'analisi
  • Jaeger-ui è un'interfaccia web per la ricerca e la visualizzazione di tracce, va a jaeger-query

Servizio Tracing, OpenTracing e Jaeger

Un componente separato può essere chiamato l'implementazione di opentrace jaeger per linguaggi specifici, attraverso i quali gli span vengono inviati a jaeger-agent.
Connessione di Jagger in Java si riduce all'implementazione dell'interfaccia io.opentracing.Tracer, dopodiché tutte le tracce attraverso di essa voleranno all'agente reale.

Servizio Tracing, OpenTracing e Jaeger

Anche per il componente a molla, puoi connetterti opentracing-spring-cloud-starter e implementazione da Jaeger opentracing-spring-jaeger-cloud-starter che configurerà automaticamente il tracciamento per tutto ciò che passa attraverso questi componenti, ad esempio richieste http ai controller, richieste al database tramite jdbc, ecc.

Tracce di registrazione in Java

Da qualche parte al livello superiore, deve essere creato il primo Span, questo può essere fatto automaticamente, ad esempio, dal controller Spring quando viene ricevuta una richiesta, o manualmente se non ce n'è. Viene quindi trasmesso attraverso l'ambito sottostante. Se uno dei metodi di seguito desidera aggiungere uno Span, prende l'attuale activeSpan dall'ambito, crea un nuovo Span e dice che il suo genitore è l'activeSpan risultante e rende attivo il nuovo Span. Quando si chiamano servizi esterni, viene passato loro l'attuale intervallo attivo e tali servizi creano nuovi intervalli con riferimento a questo intervallo.
Tutto il lavoro passa attraverso l'istanza Tracer, puoi ottenerlo tramite il meccanismo DI o GlobalTracer.get () come variabile globale se il meccanismo DI non funziona. Per impostazione predefinita, se il tracciante non è stato inizializzato, NoopTracer restituirà che non fa nulla.
Inoltre, l'ambito corrente viene ottenuto dal tracciante tramite ScopeManager, viene creato un nuovo ambito da quello corrente con un'associazione del nuovo intervallo, quindi l'ambito creato viene chiuso, che chiude l'intervallo creato e restituisce l'ambito precedente a lo stato attivo. Lo scope è legato a un thread, quindi durante la programmazione multithread, non bisogna dimenticare di trasferire lo span attivo su un altro thread, per un'ulteriore attivazione dello Scope di un altro thread con riferimento a questo span.

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

Per la programmazione multi-thread, esiste anche TracedExecutorService e wrapper simili che inoltrano automaticamente l'intervallo corrente al thread quando vengono avviate attività asincrone:

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

Per le richieste http esterne c'è TracingHttpClient

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

Problemi che abbiamo affrontato

  • Bean e DI non funzionano sempre se il tracciante non viene utilizzato in un servizio o componente, quindi Autocablato Tracer potrebbe non funzionare e dovrai utilizzare GlobalTracer.get().
  • Le annotazioni non funzionano se non è un componente o un servizio o se il metodo viene chiamato da un metodo adiacente della stessa classe. Devi stare attento a controllare cosa funziona e utilizzare la creazione manuale della traccia se @Traced non funziona. Puoi anche collegare un compilatore aggiuntivo per le annotazioni Java, quindi dovrebbero funzionare ovunque.
  • Nella vecchia primavera e avvio primaverile, l'autoconfigurazione del cloud primaverile opentraing non funziona a causa di bug in DI, quindi se vuoi che le tracce nei componenti primaverili funzionino automaticamente, puoi farlo per analogia con 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 con le risorse non funziona in groovy, devi usare prova finalmente.
  • Ogni servizio deve avere il proprio spring.application.name sotto il quale verranno registrate le tracce. Cosa significa un nome separato per la vendita e il test, in modo da non interferire con loro insieme.
  • Se utilizzi GlobalTracer e Tomcat, tutti i servizi in esecuzione in questo Tomcat hanno un GlobalTracer, quindi avranno tutti lo stesso nome di servizio.
  • Quando si aggiungono tracce a un metodo, è necessario assicurarsi che non venga chiamato molte volte in un ciclo. È necessario aggiungere una traccia comune per tutte le chiamate, che garantisca il tempo totale di lavoro. In caso contrario, verrà creato un carico in eccesso.
  • Una volta in jaeger-ui, sono state fatte richieste troppo grandi per un gran numero di tracce e, poiché non hanno aspettato una risposta, l'hanno fatto di nuovo. Di conseguenza, jaeger-query ha iniziato a consumare molta memoria e rallentare l'elastico. Aiutato riavviando jaeger-query

Campionamento, archiviazione e visualizzazione delle tracce

Ci sono tre tipi tracce di campionamento:

  1. Const che invia e salva tutte le tracce.
  2. Probabilistico che filtra le tracce con una data probabilità.
  3. Ratelimiting che limita il numero di tracce al secondo. Puoi configurare queste impostazioni sul client, sia sul jaeger-agent che sul collector. Ora usiamo const 1 nello stack del valutatore, poiché non ci sono molte richieste, ma richiedono molto tempo. In futuro, se questo eserciterà un carico eccessivo sul sistema, è possibile limitarlo.

Se usi cassandra, per impostazione predefinita memorizza solo le tracce per due giorni. Stiamo usando elasticsearch e le tracce vengono memorizzate per sempre e non vengono eliminate. Viene creato un indice separato per ogni giorno, ad esempio jaeger-service-2019-03-04. In futuro, sarà necessario configurare la pulizia automatica delle vecchie tracce.

Per visualizzare le tracce è necessario:

  • Seleziona il servizio in base al quale desideri filtrare le tracce, ad esempio tomcat7-default per un servizio che è in esecuzione in Tomcat e non può avere un proprio nome.
  • Quindi seleziona l'operazione, l'intervallo di tempo e il tempo minimo di operazione, ad esempio da 10 secondi, per eseguire solo lunghe esecuzioni.
    Servizio Tracing, OpenTracing e Jaeger
  • Vai a una delle tracce e guarda cosa stava rallentando lì.
    Servizio Tracing, OpenTracing e Jaeger

Inoltre, se è noto un ID richiesta, è possibile trovare una traccia in base a questo ID tramite una ricerca di tag, se questo ID è registrato nell'intervallo di traccia.

Documentazione

Articoli

Video

Fonte: habr.com

Aggiungi un commento