Seguimiento de servicios, OpenTracing y Jaeger

Seguimiento de servicios, OpenTracing y Jaeger

Utilizamos arquitectura de microservicios en nuestros proyectos. Cuando se producen cuellos de botella en el rendimiento, se dedica mucho tiempo a supervisar y analizar los registros. Cuando se registran los tiempos de operaciones individuales en un archivo de registro, suele ser difícil comprender qué condujo a la invocación de estas operaciones, rastrear la secuencia de acciones o el cambio de tiempo de una operación en relación con otra en diferentes servicios.

Para minimizar el trabajo manual, decidimos utilizar una de las herramientas de rastreo. Acerca de cómo y por qué puede usar el rastreo y cómo lo hicimos, y se discutirá en este artículo.

¿Qué problemas se pueden resolver con el rastreo?

  1. Encuentre cuellos de botella de rendimiento tanto dentro de un solo servicio como en todo el árbol de ejecución entre todos los servicios participantes. Por ejemplo:
    • Muchas llamadas cortas consecutivas entre servicios, por ejemplo, a geocodificación oa una base de datos.
    • Largas esperas de E/S, como transferencias de red o lecturas de disco.
    • Análisis de datos largos.
    • Operaciones largas que requieren CPU.
    • Secciones de código que no son necesarias para obtener el resultado final y que pueden eliminarse o retrasarse.
  2. Comprenda claramente en qué secuencia se llama y qué sucede cuando se realiza la operación.
    Seguimiento de servicios, OpenTracing y Jaeger
    Se puede ver que, por ejemplo, la solicitud llegó al servicio WS -> el servicio WS complementó los datos a través del servicio R -> luego envió una solicitud al servicio V -> el servicio V cargó una gran cantidad de datos desde el Servicio R -> fue al servicio P -> el servicio P fue nuevamente al servicio R -> el servicio V ignoró el resultado y fue al servicio J -> y solo luego devolvió la respuesta al servicio WS, mientras continuaba calculando algo más en el fondo.
    Sin tal rastro o documentación detallada para todo el proceso, es muy difícil entender lo que sucede cuando se mira el código por primera vez, y el código está disperso en diferentes servicios y escondido detrás de un montón de contenedores e interfaces.
  3. Recopilación de información sobre el árbol de ejecución para su posterior análisis en diferido. En cada etapa de ejecución, puede agregar información al seguimiento que está disponible en esta etapa y luego averiguar qué datos de entrada llevaron a un escenario similar. Por ejemplo:
    • ID de usuario
    • Los derechos
    • Tipo de método seleccionado
    • Error de registro o ejecución
  4. Convertir los rastros en un subconjunto de métricas y un mayor análisis ya en forma de métricas.

Qué rastro puede registrar. Durar

En el rastreo existe el concepto de un lapso, esto es un análogo de un registro, a la consola. El balneario cuenta con:

  • Nombre, generalmente el nombre del método que se ejecutó
  • El nombre del servicio en el que se generó el lapso.
  • Identificación única propia
  • Algún tipo de metainformación en forma de clave/valor que se ha registrado en él. Por ejemplo, los parámetros del método o el método terminó con un error o no.
  • Horas de inicio y finalización de este lapso
  • Id. de tramo principal

Cada intervalo se envía al recopilador de intervalos para que se almacene en la base de datos para su posterior revisión tan pronto como haya completado su ejecución. En el futuro, puede crear un árbol de todos los tramos conectándose por identificación principal. Al analizar, puede encontrar, por ejemplo, todos los lapsos en algún servicio que tomó más de un tiempo. Además, al ir a un lapso específico, vea el árbol completo por encima y por debajo de este lapso.

Seguimiento de servicios, OpenTracing y Jaeger

Opentrace, Jagger y cómo lo implementamos para nuestros proyectos

Hay un estándar común trazo abierto, que describe cómo y qué se debe recopilar, sin estar vinculado por el seguimiento a una implementación específica en ningún idioma. Por ejemplo, en Java, todo el trabajo con rastros se lleva a cabo a través de la API Opentrace común, y debajo de ella, por ejemplo, se puede ocultar Jaeger o una implementación predeterminada vacía que no hace nada.
Estamos usando Jaeger como una implementación de Opentrace. Consta de varios componentes:

Seguimiento de servicios, OpenTracing y Jaeger

  • Jaeger-agent es un agente local que generalmente se instala en cada máquina y los servicios se registran en el puerto predeterminado local. Si no hay ningún agente, los rastros de todos los servicios en esta máquina generalmente están deshabilitados
  • Jaeger-collector: todos los agentes le envían los rastros recopilados y los coloca en la base de datos seleccionada.
  • La base de datos es su cassandra preferida, pero usamos elasticsearch, hay implementaciones para un par de otras bases de datos y una implementación en memoria que no guarda nada en el disco
  • Jaeger-query es un servicio que va a la base de datos y devuelve los rastros ya recopilados para su análisis.
  • Jaeger-ui es una interfaz web para buscar y ver rastros, va a jaeger-query

Seguimiento de servicios, OpenTracing y Jaeger

Se puede llamar a un componente separado la implementación de opentrace jaeger para lenguajes específicos, a través del cual se envían intervalos a jaeger-agent.
Conexión de Jagger en Java se reduce a implementar la interfaz io.opentracing.Tracer, después de lo cual todos los rastros volarán al agente real.

Seguimiento de servicios, OpenTracing y Jaeger

También para el componente de resorte, puede conectar opentracing-primavera-nube-arrancador e implementación de Jaeger opentracing-primavera-jaeger-arrancador de nubes que configurará automáticamente el seguimiento de todo lo que pasa por estos componentes, por ejemplo, solicitudes http a los controladores, solicitudes a la base de datos a través de jdbc, etc.

Registro de rastros en Java

En algún lugar del nivel superior, se debe crear el primer tramo; esto se puede hacer automáticamente, por ejemplo, mediante el controlador de resorte cuando se recibe una solicitud, o manualmente si no hay ninguna. Luego se transmite a través del Scope a continuación. Si alguno de los métodos a continuación desea agregar un Span, toma el Span activo actual del Scope, crea un Span nuevo y dice que su padre es el Span activo resultante, y activa el Span nuevo. Al llamar a servicios externos, se les pasa el tramo activo actual y esos servicios crean nuevos tramos con referencia a este tramo.
Todo el trabajo pasa por la instancia de Tracer, puede obtenerlo a través del mecanismo DI o GlobalTracer.get () como una variable global si el mecanismo DI no funciona. De forma predeterminada, si el rastreador no se ha inicializado, NoopTracer regresará, lo que no hace nada.
Además, el alcance actual se obtiene del rastreador a través de ScopeManager, se crea un nuevo alcance a partir del actual con un enlace del nuevo tramo y luego se cierra el alcance creado, lo que cierra el tramo creado y devuelve el alcance anterior a el estado activo. El alcance está vinculado a un subproceso, por lo que cuando se programa con subprocesos múltiples, no debe olvidar transferir el intervalo activo a otro subproceso, para una mayor activación del Alcance de otro subproceso 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 la programación de subprocesos múltiples, también existe TracedExecutorService y contenedores similares que reenvían automáticamente el intervalo actual al subproceso cuando se inician tareas asincrónicas:

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

Para solicitudes http externas hay SeguimientoHttpClient

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

Problemas que enfrentamos

  • Beans y DI no siempre funcionan si el rastreador no se usa en un servicio o componente, entonces Autocableado Es posible que Tracer no funcione y tendrá que usar GlobalTracer.get().
  • Las anotaciones no funcionan si no es un componente o servicio, o si el método se llama desde un método vecino de la misma clase. Debe tener cuidado para verificar qué funciona y usar la creación manual de seguimiento si @Traced no funciona. También puede adjuntar un compilador adicional para las anotaciones de Java, luego deberían funcionar en todas partes.
  • En el antiguo spring y spring boot, la configuración automática de opentraing spring cloud no funciona debido a errores en DI, luego, si desea que las trazas en los componentes de spring funcionen automáticamente, puede hacerlo por analogí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
  • Try with resources no funciona en Groovy, debes usar Try finalmente.
  • Cada servicio debe tener su propio spring.application.name bajo el cual se registrarán los seguimientos. Lo que hace un nombre separado para la venta y la prueba, para no interferir con ellos juntos.
  • Si usa GlobalTracer y tomcat, todos los servicios que se ejecutan en este tomcat tienen un GlobalTracer, por lo que todos tendrán el mismo nombre de servicio.
  • Al agregar seguimientos a un método, debe asegurarse de que no se llame muchas veces en un ciclo. Es necesario agregar un seguimiento común para todas las llamadas, lo que garantiza el tiempo total de trabajo. De lo contrario, se creará un exceso de carga.
  • Una vez en jaeger-ui, se hicieron solicitudes demasiado grandes para una gran cantidad de rastros, y como no esperaron una respuesta, lo hicieron nuevamente. Como resultado, jaeger-query comenzó a consumir mucha memoria y ralentizar el elástico. Ayudado al reiniciar jaeger-query

Muestreo, almacenamiento y visualización de trazas

Hay tres tipos trazas de muestreo:

  1. Const que envía y guarda todos los rastros.
  2. Probabilístico que filtra rastros con alguna probabilidad dada.
  3. Ratelimiting que limita el número de trazas por segundo. Puede configurar estos ajustes en el cliente, ya sea en el agente jaeger o en el recopilador. Ahora usamos const 1 en la pila del valorador, ya que no hay muchas solicitudes, pero toman mucho tiempo. En el futuro, si esto ejercerá una carga excesiva en el sistema, puede limitarlo.

Si usa cassandra, de manera predeterminada solo almacena rastros durante dos días. Estamos usando elasticsearch y los rastros se almacenan para siempre y no se eliminan. Se crea un índice separado para cada día, por ejemplo, jaeger-service-2019-03-04. En el futuro, deberá configurar la limpieza automática de rastros antiguos.

Para ver las trazas necesitas:

  • Seleccione el servicio por el que desea filtrar los seguimientos, por ejemplo, tomcat7-default para un servicio que se ejecuta en tomcat y no puede tener su propio nombre.
  • Luego seleccione la operación, el intervalo de tiempo y el tiempo mínimo de operación, por ejemplo de 10 segundos, para tomar solo ejecuciones largas.
    Seguimiento de servicios, OpenTracing y Jaeger
  • Vaya a uno de los rastros y vea qué se estaba desacelerando allí.
    Seguimiento de servicios, OpenTracing y Jaeger

Además, si se conoce algún ID de solicitud, puede encontrar un seguimiento por este ID a través de una búsqueda de etiquetas, si este ID está registrado en el intervalo de seguimiento.

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

Artículos

Vídeo

Fuente: habr.com

Añadir un comentario