¡Increíble! 40 imágenes para comprender el principio y la práctica del sistema de seguimiento distribuido

Autor | Código Mar
Fuente | Código Mar

En la arquitectura de microservicio, una solicitud a menudo implica varios módulos, varios middleware y varias máquinas para completar la colaboración.

En esta serie de solicitudes de llamada, algunas son en serie y otras en paralelo.Entonces, ¿cómo determinar qué aplicaciones, módulos, nodos y el orden de llamada se llaman detrás de esta solicitud? ¿Cómo localizar el problema de rendimiento de cada módulo? Este artículo le revelará la respuesta.

Este artículo explicará desde los siguientes aspectos:

  • Principio y función del sistema de seguimiento distribuido

  • Principio y diseño arquitectónico de SkyWalking

  • La práctica de nuestra empresa en la cadena de llamadas distribuidas

Principio y función del sistema de seguimiento distribuido

Cómo medir el rendimiento de una interfaz, generalmente prestaremos atención a al menos los siguientes tres indicadores:

  • ¿Cómo conoces el RT de la interfaz?

  • ¿Hay una respuesta anormal?

  • ¿Dónde está la principal lenta?

Arquitectura monolítica

En los primeros días, cuando la empresa acaba de comenzar, puede adoptar la siguiente arquitectura monolítica: Para la arquitectura monolítica, ¿qué método deberíamos utilizar para calcular los tres indicadores anteriores?

La cosa más fácil de pensar es obviamente usar AOP:

Utilice AOP para imprimir el tiempo antes y después de llamar a la lógica empresarial específica para calcular el tiempo total de la llamada. Utilice AOP para detectar la excepción y también saber dónde la llamada provocó la excepción.

Arquitectura de microservicio

En la arquitectura monolítica, dado que todos los servicios y componentes están en una sola máquina, estos indicadores de monitoreo son relativamente fáciles de implementar. Sin embargo, con el rápido desarrollo de los negocios, la arquitectura monolítica se desarrollará inevitablemente hacia una arquitectura de microservicios, de la siguiente manera:

Como se muestra en la imagen: una arquitectura de microservicio un poco más compleja

Si algunos usuarios informan que una página es lenta, sabemos que la cadena de llamadas de solicitud de esta página es A -----> C -----> B -----> D, cómo ubicar qué módulo puede estar en este momento El problema causado. Cada servicio Servicio A, B, C, D tiene varias máquinas. ¿Cómo sé a qué máquina llama al servicio una determinada solicitud?

Se puede ver claramente que debido a la incapacidad de ubicar con precisión la ruta exacta por la que pasa cada solicitud, existen varios puntos débiles en la arquitectura de microservicios:

  1. Dificultad en la resolución de problemas y ciclo largo

  2. Difícil de reproducir escenas específicas

  3. El análisis de los cuellos de botella del rendimiento del sistema es difícil

La cadena de llamadas distribuidas nació para resolver los problemas anteriores, y sus principales funciones son las siguientes:

  • Toma datos automáticamente  

  • Analice los datos para generar una cadena de llamadas completa: con una cadena de llamadas completa de la solicitud, el problema tiene una alta probabilidad de repetición

  • Visualización de datos: la visualización del rendimiento de cada componente puede ayudarnos a localizar el cuello de botella del sistema y descubrir el problema a tiempo

A través del sistema de seguimiento distribuido, cada enlace de solicitud específico de las siguientes solicitudes se puede ubicar bien, de modo que el seguimiento del enlace de solicitud se puede realizar fácilmente, y el cuello de botella de rendimiento de cada módulo se puede localizar y analizar.

Cadena de llamada distribuida estándar-OpenTracing

Conociendo el papel de las cadenas de llamadas distribuidas, echemos un vistazo a cómo implementar la implementación y los principios de las cadenas de llamadas distribuidas. En primer lugar, para resolver el problema de la incompatibilidad de API de diferentes sistemas de seguimiento distribuido, nació la especificación OpenTracing. OpenTracing es un sistema ligero Nivel de la capa de estandarización, se encuentra entre la biblioteca de aplicaciones / clases y el programa de seguimiento o análisis de registros.

De esta manera, OpenTracing proporciona API independientes de la plataforma y del proveedor para que los desarrolladores puedan agregar fácilmente la implementación del sistema de seguimiento.

Hablando de esto, ¿alguna vez has pensado en una implementación similar en Java? Recuerde JDBC, al proporcionar un conjunto de interfaces estándar para que las implementen varios proveedores, los programadores pueden enfrentarse a la programación de interfaces sin preocuparse por la implementación específica.

La interfaz aquí es en realidad un estándar, por lo que es muy importante formular un conjunto de estándares para habilitar componentes conectables.

A continuación, veamos el modelo de datos de OpenTracing, existen principalmente los siguientes tres:

  • Seguimiento: un enlace de solicitud completo

  • Intervalo: un proceso de llamada (se requieren la hora de inicio y la hora de finalización)

  • SpanContext: rastrea información de contexto global, como traceId en ella

Es muy importante entender estos tres conceptos. Para que todos puedan entender mejor estos tres conceptos, hice un dibujo especial:

Como se muestra en la figura, la solicitud completa de un pedido es un seguimiento completo. Obviamente, para esta solicitud, debe haber un identificador global para identificar esta solicitud. Cada llamada se denomina Span y cada llamada debe ser traída. El TraceId global, para que a cada llamada se pueda asociar el TraceId global Este TraceId se transmite a través del SpanContext, ya que es necesario transmitir, obviamente se debe llamar de acuerdo con el protocolo.

Como se muestra en la figura, comparamos el protocolo de transmisión con un automóvil, SpanContext con mercancías y Span con carreteras. Debería ser mejor entenderlo.

Después de comprender estos tres conceptos, déjeme ver cómo el sistema de seguimiento distribuido recopila la cadena de llamadas de microservicio en el gráfico unificado:

Podemos ver que hay un recopilador en la capa inferior que ha estado recopilando datos en la oscuridad, entonces, ¿qué información se recopilará cada vez que se llame al recopilador?

  1. Trace_id global: esto es obvio, por lo que cada sub-llamada se puede asociar con la solicitud original

  2. span_id: 0, 1, 1.1, 2 en la figura, para que pueda identificar qué llamada

  3. parent_span_id: Por ejemplo, el span_id de b que llama a d es 1.1, luego su parent_span_id es el span_id de un b que llama, que es 1, de modo que se pueden asociar dos llamadas adyacentes.

Con esta información, la información que recopila Collector para cada llamada es la siguiente:

Con base en la información de estos gráficos, es obvio que la vista visual de la cadena de llamadas se puede dibujar de la siguiente manera:

Así se realiza un completo sistema de seguimiento distribuido.

La implementación anterior parece realmente simple, pero hay varios problemas que requieren que pensemos con cuidado:

  1. Cómo recopilar datos de tramo automáticamente: recopilación automática, sin invasión del código comercial

  2. Cómo transferir contexto a través de procesos

  3. Cómo garantizar la unicidad global de traceId

  4. ¿Afectarán tantas solicitudes al rendimiento?

A continuación, déjeme ver cómo SkyWalking resuelve los cuatro problemas anteriores.

Principio y diseño arquitectónico de SkyWalking


Cómo recopilar datos de tramo automáticamente

SkyWalking adopta la forma de plug-in + javaagent para realizar la recopilación automática de datos de tramo, de modo que no sea invasivo para el código , plug-in significa enchufable, buena extensibilidad (a continuación se presentará cómo definir su propio plug-in ).

Cómo transferir contexto a través de procesos

Sabemos que los datos generalmente se dividen en encabezado y cuerpo, al igual que http tiene encabezado y cuerpo, RocketMQ también tiene MessageHeader, Message Body, el cuerpo generalmente contiene datos comerciales, por lo que no es adecuado para pasar contexto en el cuerpo, pero debe pasar contexto en el encabezado, como se muestra en la figura :

El archivo adjunto en dubbo es equivalente al encabezado, por lo que colocamos el contexto en el archivo adjunto, lo que resuelve el problema de transferencia de contexto.

Consejos: El proceso de transferencia de contexto aquí se maneja en el complemento dubbo, y no hay percepción comercial. Cómo se implementa este complemento, lo analizaré a continuación.

Cómo garantizar la unicidad global de traceId

Para garantizar la unicidad global, podemos usar ID distribuidos o generados localmente. Si usa ID distribuidos, debe tener un remitente. Cada vez que lo solicite, primero debe solicitar al remitente. Habrá una sobrecarga de llamadas de red, por lo que SkyWalking eventualmente Utiliza el método de generación de ID localmente, utiliza el famoso algoritmo de flujo de nieve y tiene un alto rendimiento.

Ilustración: identificación generada por el algoritmo de copo de nieve

Sin embargo, el algoritmo del copo de nieve tiene un problema bien conocido: la devolución de llamada de tiempo , que puede causar una generación de identificación duplicada. Entonces, ¿cómo resuelve SkyWalking el problema de devolución de llamada de tiempo?

Cada vez que se genera una identificación, se registra la hora en la que se generó la identificación (lastTimestamp). Si la hora actual es menor que la hora en que se generó la identificación por última vez (lastTimestamp), significa que se ha producido una devolución de llamada y se generará un número aleatorio como traceId.

Puede que haya estudiantes que quieran ser más reales aquí, y pueden sentir que el número aleatorio generado también será el mismo que el ID global generado. Sería mejor si agrega otra capa de verificación.

Aquí quiero hablar sobre la elección del diseño del sistema. En primer lugar, si se verifica la unicidad del número aleatorio generado, indudablemente habrá una capa adicional de llamada y habrá una cierta pérdida de rendimiento.

Pero, de hecho, la probabilidad de devolución de llamada de tiempo es muy pequeña (después de la ocurrencia del desorden de tiempo de la máquina, el negocio se verá muy afectado, por lo que el ajuste del tiempo de la máquina debe ser cauteloso), y la probabilidad de coincidencia de los números aleatorios generados también es muy pequeña. , Teniendo en cuenta que realmente no es necesario agregar otra capa de verificación de singularidad global.

Para la selección de soluciones técnicas, debemos evitar el diseño excesivo.

Con tantas solicitudes, ¿afectará toda la recopilación al rendimiento?

Si llama para cada solicitud para recopilar, entonces no hay duda de que la cantidad de datos será muy grande, pero a su vez, piense si realmente es necesario recopilar para cada solicitud. De hecho, no es necesario. Podemos establecer la frecuencia de muestreo y solo muestrear Para parte de los datos, SkyWalking tiene como valor predeterminado muestrear 3 veces en 3 segundos, y otras solicitudes no se muestrean, como se muestra en la figura:

Este tipo de frecuencia de muestreo es suficiente para que analicemos el rendimiento del componente ¿Qué problemas habrá al muestrear datos con una frecuencia de 3 veces en 3 segundos? Idealmente, cada llamada de servicio se realiza en el mismo momento (como se muestra en la figura siguiente), por lo que está bien tomar muestras en el mismo momento cada vez.

Sin embargo, en producción, es básicamente imposible que se llame a cada llamada de servicio en el mismo momento, porque hay demoras en las llamadas de red durante el período y es probable que la situación real de la llamada sea la siguiente:

En este caso, algunas llamadas se muestrearán en el servicio A, pero no en los servicios B y C, y es imposible analizar el rendimiento de la cadena de llamadas. Entonces, ¿cómo lo resuelve SkyWalking?

Se resuelve así: si el flujo ascendente transporta Contexto (que indica muestreo ascendente), el flujo descendente se ve obligado a recopilar datos. Esto puede garantizar la integridad del enlace.

Infraestructura de SkyWalking

La estructura básica de SkyWalking es la siguiente: se puede decir que casi todas las llamadas distribuidas se componen de los siguientes componentes.

En primer lugar, por supuesto, el muestreo regular de los datos de los nodos. Después del muestreo, los datos se informan regularmente y se almacenan en la capa de persistencia, como ES y MySQL. Con los datos, es natural hacer un análisis visual basado en los datos.

¿Cómo funciona SkyWalking?

A continuación, todos deben estar más preocupados por el rendimiento de SkyWalking, luego echemos un vistazo a los datos de evaluación oficiales:

El azul en la figura representa el rendimiento sin SkyWalking y el naranja representa el rendimiento con SkyWalking. Los datos anteriores se miden con un TPS de 5000. Se puede ver que ya sea CPU, memoria o tiempo de respuesta, use SkyWalking con La pérdida de rendimiento que se produce es casi insignificante.

A continuación, veamos la comparación entre SkyWalking y Zipkin, Pinpoint, otra herramienta de seguimiento distribuida conocida en la industria (comparación con una frecuencia de muestreo por segundo, 500 subprocesos y 5000 solicitudes en total). Se puede ver que Zipkin (117 ms) y PinPoint (201 ms) son muy inferiores a SkyWalking (22 ms) en términos de tiempo de respuesta crítico.

Desde el índice de pérdida de rendimiento, ¡SkyWalking ha ganado!

Otro aspecto en el indicador: cómo el código invasivo, requiere Zipkin enterrado en el punto de aplicación, fuerte invasión del código, utilizando el SkyWalking javaagent + Reproductor de tales modificaciones de código de bytes maneras de hacer para No hay intrusión de código . Además del rendimiento y la intrusión del código, SkyWaking funciona bien. También tiene las siguientes ventajas:

  • Compatibilidad con varios idiomas, componentes enriquecidos: actualmente admite Java, .Net Core, PHP, NodeJS, Golang, lenguajes LUA, y los componentes también admiten componentes comunes como dubbo, mysql y la mayoría de ellos pueden satisfacer nuestras necesidades.

  • Extensibilidad: para los complementos insatisfechos, podemos escribir uno manualmente de acuerdo con las reglas de SkyWalking, y los complementos recién implementados no invadirán el código.

Practica en la cadena de llamadas distribuidas


El marco de aplicación de SkyWalking en nuestra empresa

De lo anterior, podemos ver que SkyWalking tiene muchas ventajas, entonces, ¿hemos utilizado todos sus componentes? De hecho, no lo es. Echemos un vistazo a su arquitectura de aplicaciones en nuestra empresa:

Se puede ver en la figura que solo usamos el agente de SkyWalking para el muestreo y abandonamos los otros tres componentes de "informes y análisis de datos", "almacenamiento de datos" y "visualización de datos", así que ¿por qué no usar directamente todo el conjunto de SkyWalking? La solución se debe a que nuestro ecosistema de monitoreo de Marvin estaba relativamente completo antes de conectarnos a SkyWalking.

Si lo reemplaza con SkyWalking, no es necesario. Marvin puede satisfacer nuestras necesidades en la mayoría de los escenarios. En segundo lugar, el costo de reemplazo del sistema es alto. En tercer lugar, si se vuelve a conectar con el usuario, el costo de aprendizaje es alto.

Esto también nos da una idea: es muy importante que cualquier producto aproveche la oportunidad, y el costo de reemplazo de los productos posteriores será alto. Aprovechar la primera oportunidad significa apoderarse de la mente del usuario. Esto es como WeChat. Aunque la interfaz de usuario está bien hecha, es Whatsapp no ​​se puede hacer en países extranjeros, porque la primera oportunidad se ha ido.

Por otro lado, para la arquitectura, no existe lo mejor, sino lo más adecuado. La esencia del diseño arquitectónico es equilibrar las compensaciones con los escenarios comerciales actuales.

Qué transformaciones y prácticas ha realizado nuestra empresa en SkyWalking

Nuestra empresa realizó principalmente las siguientes transformaciones y prácticas:

  1. El entorno previo al lanzamiento requiere un muestreo obligatorio debido a la depuración

  2. ¿Conseguir un muestreo más detallado?

  3. TraceId incrustado en el registro

  4. Implementación de desarrollo propio del complemento SkyWalking

El entorno previo al lanzamiento requiere un muestreo obligatorio debido a la depuración

A partir del análisis anterior, podemos ver que Collector está muestreando regularmente en segundo plano. ¿No es bueno? ¿Por qué necesitamos implementar un muestreo forzado?

Todavía está por solucionar problemas de posicionamiento. A veces hay problemas en línea. Esperamos reproducirlo en el pre-lanzamiento, esperando ver la cadena de llamadas completa de esta solicitud, por lo que es necesario implementar muestreo forzado en el pre-lanzamiento. Así que modificamos el complemento dubbo de Skywalking para implementar el muestreo obligatorio.

Traemos un par clave-valor similar a force_flag = true en la cookie solicitada para indicar que queremos forzar el muestreo. Una vez que la puerta de enlace recibe esta cookie, traerá el par clave-valor force_flag = true en el adjunto dubbo, y luego El complemento dubbo de skywalking puede juzgar si se trata de muestreo forzado basándose en esto. Si existe este valor, significa muestreo forzado. Si no existe tal valor, tomará un muestreo de tiempo normal.

¿Conseguir un muestreo más detallado?

Ha llamado muestreo más detallado. Primero veamos el método de muestreo predeterminado de skywalking, es decir, muestreo unificado.

Sabemos que este método se predetermina a 3 veces antes de muestrear en 3 segundos, y todas las demás solicitudes se descartan. En este caso, hay un problema. Supongamos que hay varias llamadas dubbo, mysql, redis en 3 segundos en esta máquina, pero si las primeras tres veces Si todas son llamadas dubbo, no se pueden muestrear otras llamadas como mysql, redis, etc., por lo que modificamos skywalking para lograr el muestreo grupal, de la siguiente manera:

Es decir, se muestrean 3 veces en 3 segundos redis, dubbo, mysql, etc., lo que evita este problema.

¿Cómo incrustar traceId en el registro?

El traceId incrustado en el registro de salida es conveniente para que podamos solucionar problemas, por lo que es muy necesario escribir el traceId. ¿Cómo incrustar el traceId en el registro?

Estamos usando log4j. Aquí debemos entender el mecanismo del complemento log4j. Log4j nos permite personalizar el complemento para generar el formato de registro. Primero, debemos definir el formato de registro e incrustar% traceId en el formato de registro personalizado como la cuenta Símbolos, como sigue:

Luego implementamos un complemento log4j, de la siguiente manera:

Primero, el complemento log4j necesita definir una clase que hereda la clase LogEventPatternConverter y se declara como un complemento con el complemento estándar. El marcador de posición que se reemplazará se especifica mediante la anotación @ConverterKeys y luego se reemplaza en el método de formato. Soltar.

De esta forma, el TraceId que queremos aparecerá en el log, de la siguiente manera:

Qué complementos de skywalking han sido desarrollados por nuestra empresa

SkyWalking ha implementado muchos complementos, pero los complementos de memcached y druid no se proporcionan, por lo que desarrollamos estos dos complementos de acuerdo con sus especificaciones.

Cómo implementar el complemento, puede ver que se compone principalmente de tres partes:

  1. Clase de definición de complemento: especifique la clase de definición del complemento. Finalmente, el complemento se empaquetará y generará de acuerdo con la clase de definición aquí

  2. Instrumentación: especifique el aspecto, el punto de contacto, qué método de qué clase debe mejorarse

  3. Interceptor, especifique el paso 2 para escribir lógica mejorada en la parte delantera, trasera o excepción del método

Tal vez aún no lo entiendas después de leerlo. Vamos a explicarlo brevemente con el complemento dubbo. Sabemos que en el servicio dubbo, cada solicitud recibe un mensaje de netty y lo envía al grupo de subprocesos comerciales para su procesamiento, hasta la llamada real al final del método comercial. Después de más de una docena de procesamiento de filtros en el medio.

El MonitorFilter puede interceptar todas las solicitudes del cliente o las solicitudes de procesamiento del lado del servidor, por lo que podemos mejorar el MonitorFilter, antes de llamar al método invoke, inyectar el traceId global en el archivo adjunto de su Invocación, para asegurar que la solicitud llegue al real El traceId global ya existe antes de la lógica empresarial.

Entonces, obviamente, necesitamos especificar la clase que queremos mejorar (MonitorFilter) en el complemento y mejorar su método (invocar). ¿Qué mejoras se deben realizar en este método? Esto es lo que hace el interceptor (Inteceptor), echemos un vistazo La instrumentación (DubboInstrumentation) en el complemento Dubbo.

Echemos un vistazo a lo que hace el Inteceptor descrito en el código. Los pasos clave se enumeran a continuación:

En primer lugar, beforeMethod representa que el método se llamará antes de que se ejecute el método de invocación de MonitorFilter, y el correspondiente es afterMethod, que representa la lógica de mejora después de que se ejecute el método de invocación.

En segundo lugar, podemos ver en los puntos 2 y 3 que, ya sea un consumidor o un proveedor, el ID global se procesa en consecuencia, para asegurar que el traceid global esté garantizado cuando se alcance la capa de negocio real. Después de que se definan la Instrumentación y el Interceptor , El último paso es especificar la clase definida en skywalking.def.

// skywalking-plugin.def 文件dubbo=org.apache.skywalking.apm.plugin.asf.dubbo.DubboInstrumentation

El complemento empaquetado mejorará el método de invocación de MonitorFilter y realizará operaciones como la inyección de traceId global en el adjunto del período antes de que se ejecute el método de invocación. Todos estos son silenciosos y no intrusivos en el código.

para resumir

Este artículo presenta el principio del sistema de seguimiento distribuido desde lo más superficial a lo más profundo. Creo que todos tienen una comprensión más profunda de su función y mecanismo de trabajo.

Es particularmente importante tener en cuenta que al introducir una determinada técnica, debemos combinar la arquitectura técnica existente para hacer la elección más razonable. Al igual que SkyWalking tiene cuatro módulos, nuestra empresa solo utiliza su función de muestreo de agentes. No existe la mejor técnica, solo La tecnología más adecuada.

A través de este artículo, creo que todos deberían tener una comprensión más clara del mecanismo de implementación de SkyWalking. Este artículo solo presenta la implementación de los complementos de SkyWalking, pero, después de todo, es un software de grado industrial. Para comprender su extensión y profundidad, debe leer más código fuente.


更多精彩推荐
☞谷歌软件工程师薪资百万,大厂薪资有多高?
☞这都是啥软件?你能猜到吗?| 每日趣闻
☞杜甫在线演唱《奇迹再现》、兵马俑真人还原……用AI技术打破次元壁的大谷来参加腾讯全球数字生态大会啦!
☞开放源码,华为鸿蒙HarmonyOS 2.0来了
☞20张图,带你搞懂高并发中的线程与线程池!
☞跨链,该怎么跨?
点分享点点赞点在看

Supongo que te gusta

Origin blog.csdn.net/csdnnews/article/details/108544144
Recomendado
Clasificación