Service Tracing, OpenTracing e Jaeger

Service Tracing, OpenTracing e Jaeger

Usamos arquitectura de microservizos nos nosos proxectos. Cando se producen colos de botella no rendemento, pásase moito tempo en supervisar e analizar os rexistros. Ao rexistrar os tempos de operacións individuais nun ficheiro de rexistro, adoita ser difícil entender o que levou á invocación destas operacións, rastrexar a secuencia de accións ou o cambio de tempo dunha operación en relación a outra en diferentes servizos.

Para minimizar o traballo manual, decidimos utilizar unha das ferramentas de rastrexo. Sobre como e por que podes usar o rastrexo e como o fixemos, e comentarase neste artigo.

Que problemas se poden resolver co trazado

  1. Atopar colos de botella de rendemento tanto dentro dun único servizo como en toda a árbore de execución entre todos os servizos participantes. Por exemplo:
    • Moitas chamadas consecutivas curtas entre servizos, por exemplo, a xeocodificación ou a unha base de datos.
    • Longas esperas de E/S, como transferencias de rede ou lecturas de disco.
    • Análise longa de datos.
    • Operacións longas que requiren CPU.
    • Seccións de código que non son necesarias para obter o resultado final e que se poden eliminar ou atrasar.
  2. Comprender claramente en que secuencia se chama e que ocorre cando se realiza a operación.
    Service Tracing, OpenTracing e Jaeger
    Pódese ver que, por exemplo, a Solicitude chegou ao servizo WS -> o servizo WS engadiu datos a través do servizo R -> despois enviou unha solicitude ao servizo V -> o servizo V cargou moitos datos do R servizo -> pasou ao servizo P -> o servizo P pasou de novo ao servizo R -> o servizo V ignorou o resultado e pasou ao servizo J -> e só entón devolveu a resposta ao servizo WS, mentres continuaba calculando outra cousa no fondo.
    Sen tal rastro ou documentación detallada para todo o proceso, é moi difícil entender o que está a suceder cando miras o código por primeira vez, e o código está espallado por diferentes servizos e escondido detrás dunha morea de papeleiras e interfaces.
  3. Recollida de información sobre a árbore de execución para a súa posterior análise diferida. En cada fase de execución, pode engadir información ao rastrexo dispoñible nesta fase e, a continuación, descubrir que datos de entrada levaron a un escenario similar. Por exemplo:
    • ID do usuario
    • Dereitos
    • Tipo de método seleccionado
    • Erro de rexistro ou de execución
  4. Convertendo os rastros nun subconxunto de métricas e máis análises xa en forma de métricas.

Que rastro pode rexistrar. Span

No trazado hai o concepto de tramo, este é un análogo dun rexistro, para a consola. O balneario ten:

  • Nome, normalmente o nome do método que se executou
  • O nome do servizo no que se xerou o intervalo
  • ID exclusivo propio
  • Algún tipo de metainformación en forma de clave/valor que se iniciou sesión nela. Por exemplo, os parámetros do método ou o método remataron cun erro ou non
  • Horas de inicio e finalización deste período
  • ID do período parental

Cada intervalo envíase ao colector de intervalos para ser almacenado na base de datos para a súa posterior revisión tan pronto como remate a súa execución. No futuro, podes construír unha árbore de todos os tramos conectándote por ID do pai. Ao analizar, podes atopar, por exemplo, todos os intervalos nalgún servizo que levou máis dun tempo. Ademais, ao ir a un tramo específico, vexa a árbore enteira por riba e por debaixo deste tramo.

Service Tracing, OpenTracing e Jaeger

Opentrace, Jagger e como o implementamos para os nosos proxectos

Hai un estándar común traza aberta, que describe como e que se debe recoller, sen estar vinculado por rastrexar a unha implementación específica en ningún idioma. Por exemplo, en Java, todo o traballo con trazos realízase a través da API común de Opentrace, e baixo ela pódese ocultar, por exemplo, Jaeger ou unha implementación predeterminada baleira que non fai nada.
Estamos usando Jaeger como implementación de Opentrace. Consta de varios compoñentes:

Service Tracing, OpenTracing e Jaeger

  • Jaeger-agent é un axente local que adoita instalarse en cada máquina e os servizos están iniciados nela no porto predeterminado local. Se non hai axente, entón os rastros de todos os servizos desta máquina adoitan estar desactivados
  • Jaeger-collector: todos os axentes envíanlle os trazos recollidos e colócaos na base de datos seleccionada
  • A base de datos é a súa cassandra preferida, pero usamos elasticsearch, hai implementacións para outras bases de datos e unha implementación en memoria que non garda nada no disco
  • Jaeger-query é un servizo que vai á base de datos e devolve os trazos xa recollidos para a súa análise
  • Jaeger-ui é unha interface web para buscar e ver trazos, vai a jaeger-query

Service Tracing, OpenTracing e Jaeger

Pódese chamar un compoñente separado a implementación de opentrace jaeger para linguaxes específicas, a través do cal os intervalos se envían a jaeger-agent.
Conectando Jagger en Java redúcese a implementar a interface io.opentracing.Tracer, despois de que todos os rastros a través dela voarán ao axente real.

Service Tracing, OpenTracing e Jaeger

Tamén para o compoñente de resorte, pode conectar opentracing-spring-cloud-starter e implementación de Jaeger opentracing-spring-jaeger-cloud-starter que configurará automaticamente o rastrexo para todo o que pasa por estes compoñentes, por exemplo solicitudes http aos controladores, solicitudes á base de datos a través de jdbc, etc.

Rastros de rexistro en Java

Nalgún lugar do nivel superior, debe crearse o primeiro Span, isto pódese facer automaticamente, por exemplo, polo controlador de resorte cando se recibe unha solicitude, ou manualmente se non a hai. Despois transmítese a través do Ámbito a continuación. Se algún método a continuación quere engadir un Span, toma o ActiveSpan actual do Ámbito, crea un novo Span e di que o seu pai é o ActiveSpan resultante e activa o novo Span. Ao chamar a servizos externos, transmítelles o intervalo activo actual e eses servizos crean novos intervalos con referencia a este intervalo.
Todo o traballo pasa pola instancia Tracer, pode obtelo a través do mecanismo DI ou GlobalTracer.get () como unha variable global se o mecanismo DI non funciona. Por defecto, se o trazador non foi inicializado, NoopTracer devolverá o que non fai nada.
Ademais, o ámbito actual obtense do rastreador a través do ScopeManager, créase un novo ámbito a partir do actual cunha ligazón do novo intervalo e, a continuación, péchase o ámbito creado, o que pecha o ámbito creado e devolve o ámbito anterior a o estado activo. O alcance está ligado a un fío, polo que cando se programa con varios fíos, non debes esquecer transferir o intervalo activo a outro fío, para unha maior activación do ámbito doutro fío con referencia a este intervalo.

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

Para a programación multiproceso, tamén hai TracedExecutorService e envoltorios similares que reenvían automaticamente o intervalo actual ao fío cando se inician tarefas asíncronas:

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

Para solicitudes http externas hai Rastrexo HttpClient

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

Problemas que nos enfrontamos

  • Os feixóns e DI non sempre funcionan se o trazador non se usa nun servizo ou compoñente, entón Cableado automático É posible que Tracer non funcione e terás que usar GlobalTracer.get().
  • As anotacións non funcionan se non é un compoñente ou servizo, ou se o método se chama desde un método veciño da mesma clase. Tes que ter coidado de comprobar o que funciona e utilizar a creación manual de rastrexo se @Traced non funciona. Tamén podes engadir un compilador adicional para as anotacións en Java, entón deberían funcionar en todas partes.
  • No antigo arranque de primavera e primavera, a configuración automática da nube de primavera de opentraing non funciona debido a erros en DI, entón se queres que os trazos nos compoñentes de primavera funcionen automaticamente, podes facelo por analoxía 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
  • Proba con recursos non funciona en groovy, debes usar try finalmente.
  • Cada servizo debe ter o seu propio spring.application.name baixo o cal se rexistrarán os rastros. O que fai un nome separado para a venda e a proba, para non interferir con eles xuntos.
  • Se usa GlobalTracer e tomcat, todos os servizos que se executan neste tomcat teñen un GlobalTracer, polo que todos terán o mesmo nome de servizo.
  • Ao engadir trazos a un método, debes asegurarte de que non se chama moitas veces nun bucle. É necesario engadir un trazo común para todas as chamadas, que garanta o tempo total de traballo. En caso contrario, crearase un exceso de carga.
  • Unha vez en jaeger-ui, realizáronse solicitudes demasiado grandes para un gran número de trazos e, como non agardaron a resposta, volvérono a facer. Como resultado, jaeger-query comezou a comer moita memoria e ralentizar o elástico. Axudado ao reiniciar jaeger-query

Toma de mostras, almacenamento e visualización de trazos

Hai tres tipos trazos de mostraxe:

  1. Const que envía e garda todos os rastros.
  2. Probabilística que filtra trazos cunha probabilidade dada.
  3. Ratelimiting que limita o número de trazos por segundo. Podes configurar estas opcións no cliente, ben no jaeger-agent ou no colector. Agora usamos const 1 na pila de avaliadores, xa que non hai moitas solicitudes, pero levan moito tempo. No futuro, se isto vai exercer unha carga excesiva no sistema, pode limitalo.

Se usas Cassandra, por defecto só almacena os rastros durante dous días. Estamos usando busca elástica e os rastros almacénanse para sempre e non se eliminan. Créase un índice separado para cada día, por exemplo, jaeger-service-2019-03-04. No futuro, cómpre configurar a limpeza automática de trazos antigos.

Para ver os trazos necesitas:

  • Seleccione o servizo polo que desexa filtrar as trazas, por exemplo, tomcat7-default para un servizo que se está a executar no tomcat e que non pode ter o seu propio nome.
  • A continuación, seleccione a operación, o intervalo de tempo e o tempo mínimo de operación, por exemplo de 10 segundos, para levar só execucións longas.
    Service Tracing, OpenTracing e Jaeger
  • Vaia a un dos rastros e mira o que estaba a diminuír alí.
    Service Tracing, OpenTracing e Jaeger

Ademais, se se coñece algún identificador de solicitude, podes atopar un rastro por este identificador mediante unha busca de etiquetas, se este identificador está rexistrado no intervalo de rastrexo.

Documentación

artigos

Vídeo

Fonte: www.habr.com

Engadir un comentario