предисловие
В процессе использования традиционной автономной системы, если ответ на запрос слишком медленный или ответ неправильный, разработчики могут четко определить, что с запросом возникла проблема, и просмотреть журнал, чтобы найти конкретный метод. Но в распределенной системе, если клиентский запрос достигает сервера, он выполняется несколькими службами. Например, служба A вызывает службу B, служба B вызывает службу C и службу D, а служба D вызывает службу E. Если вы хотите узнать, какая служба требует слишком много времени для обработки или является ненормальной, из-за чего запрос обрабатывается медленно или прерывается , просто разработчикам нужно проверять журналы на машине одного сервиса за другим, сначала найти сервис с проблемой, а потом определить конкретное место проблемы. Только представьте, по мере того, как система становится больше и становится доступным больше сервисов, цепочка вызовов сервисов, соответствующих запросу, становится все длиннее и длиннее, насколько сложен этот метод устранения неполадок. Для решения этой проблемы родились решения для отслеживания проблем в различных распределенных сценариях, и zipkin — одно из них.
Как выглядит общая структура
Независимая распределенная система слежения, клиент существует в приложении (то есть в каждом сервисе), и должен иметь функции формирования, сбора и передачи информации слежения, а сервер должен включать следующие три основные функции:
- Сбор информации: используется для сбора информации, собранной каждым сервером, сортировки, хранения и индексации информации.
- Хранение данных: хранит данные отслеживания.
- Служба запросов: Предоставляет интерфейс для запросов и запросов информации о ссылках.
Общая структура zipkin выглядит следующим образом:
zipkin (сервер) содержит четыре компонента, а именно сборщик, хранилище, поиск и веб-интерфейс.
- Сборщик — это сборщик информации.Как процесс-демон, он всегда будет ждать данных отслеживания, переданных клиентом, для проверки, сохранения и создания индексов, необходимых для запроса.
- хранилище является компонентом хранилища. zipkin данные по умолчанию непосредственно в памяти, в дополнение к поддержке использования Cassandra, ElasticSearch и Mysql.
- Поиск — это процесс запроса, который предоставляет простой JSON API для внешних вызовов запросов.
- Веб-интерфейс — это платформа отображения zipkin на стороне сервера. Он в основном вызывает интерфейс, предоставляемый поиском, и четко отображает информацию о ссылках для разработчиков с диаграммами.
Клиент zipkin в основном отвечает за создание информации о трассировке в соответствии с вызовом приложения и отправку информации о трассировке в zipkin для получения сборщиком. Поддержка каждого языка отличается, вы можете проверить деталиофициальный сайт zipkin, поддержка языка java Brave. На приведенной выше структурной схеме наличие трекера означает интеграцию Brave.
Понимание основных понятий
Прежде чем использовать zipkin, сначала разберитесь с двумя основными понятиями Trace и Span. После того, как запрос достигает приложения, цепочка вызовов, состоящая из всех служб, вызываемых всеми службами, имеет вид древовидной структуры (как показано на рисунке ниже).отслеживатьэтот звонокссылка на сайтПолученную древовидную структуру можно назватьTrace.
В трассировке каждая службакаждый звонок, этоосновная единица работы, как и каждый узел дерева на рисунке выше, назовите егоspan. Каждый пролет имеетid как уникальный идентификатор, а также каждый раз, когда Trace будет генерироватьtraceId используется в качестве идентификатора трассировки в диапазоне, а затем передатьparentId указывает инициатора этого вызова(Это span-id инициатора). Когда пролеты имеют три вышеуказанных идентификатора, несколько промежутков можно четко отсортировать и соединить последовательно, и, наконец, можно создать полную ссылку для отслеживания. Кроме того, span будет иметь другие данные, такие как: имя, контекст узла, временная метка и информация тега структуры KV и т. д. (Основные аннотации Zipkin v1, такие как «cs» и «sr», были заменены на Span.Kind, см. подробностиzipkin-api, эта статья объяснит конкретную модель данных Span после введения в демонстрацию).как отслеживать
Средство отслеживания находится в приложении и отвечает за создание соответствующего идентификатора, запись информации, необходимой для диапазона, и, наконец, передачу ее сборщику на стороне сервера через транспортный уровень. Сначала рассмотрим следующие вопросы:
- Когда генерируется основная информация, необходимая для каждого диапазона?
- Какую информацию необходимо передать поставщику услуг при вызове службы?
- Когда отправлять span на сервер zipkin?
- Как отправить спаны?
Span представляет собой сервисный вызов, тогда трекер должен срабатывать от действия, инициированного сервисным вызовом, для генерации основной информации. текущей ссылки трассировки и текущего идентификатора трассировки. Когда поставщик услуг завершает обслуживание и отвечает вызывающему абоненту, ему необходимо рассчитать продолжительность обслуживания в соответствии с отметкой времени, записанной при инициировании вызова, и текущей отметкой времени. завершено, и его можно отправить на сервер zipkin. Тем не менее, следует отметить, что отправка спана сборщику zipkin не должна влиять на результат этого дела, успешно ли отправлен спан или нет, не имеет никакого отношения к бизнесу, поэтому его нужно отправлять асинхронно. для предотвращения задержек и отказов системы слежения от отправки системного прерывания пользователя. На следующем рисунке показан процесс отслеживания вызова http-запроса (на основе блок-схемы, представленной на официальном сайте zipkin):
Видно, что когда сервис А запрашивает сервис Б, он сначала перехватывается трекером, записывается информация тега и временная метка, при этом идентификатор трекинга добавляется в http-заголовок и передается сервису Б. служба B отвечает, продолжительность записывается, и, наконец, принимается асинхронный способ отправки сборщику zipkin. Существует три основных метода доставки отрезков из отслеживаемой службы в сборщик Zipkin: http, Kafka и Scribe (система сбора журналов Facebook с открытым исходным кодом).Установите zipkin за 1 минуту
Принцип реализации распределенной системы отслеживания на основе zipkin был подробно объяснен выше.Вот краткое введение в метод установки zipkin, загрузите пакет jar и запустите его напрямую. Просто и грубо, но обратите внимание на jdk1.8 и выше. Чтобы узнать о двух других методах установки, см. официальное введение.
wget -O zipkin.jar 'https://search.maven.org/remote_content?g=io.zipkin.java&a=zipkin-server&v=LATEST&c=exec'
java -jar zipkin.jar
После успешного запуска откройте браузер для доступа к веб-интерфейсу zipkin, введите http://ip:9411/, и страница отобразится, как показано на рисунке ниже. Конкретное использование будет представлено позже.
Напишите демо для использования (Spring Boot интегрирует zipkin)
Официальных документов для Brave, клиента java-версии, очень мало, и все они есть на github. В то время для Xiaobai было головной болью найти его: если вы возьмете код в блогах, написанных великими богами в Интернете, и замените его последними зависимостями, он покажет, что эти классы помечены как устаревшие, и не рекомендуется их использовать.
- смелый исходный адрес:GitHub.com/open zip kin/…
- Официальный демо-адрес:GitHub.com/open zip kin/…
- Дружеское напоминание: в этом разделе есть много кода, комментарии довольно подробны, а текст введения меньше. Демо-структура, написанная Xiaobai, отображается на рисунке ниже. Три загрузочных приложения, Service1, Service2 и Service3 создаются соответственно, а смелая интеграционная часть используется в качестве модуля, так что его можно встроить в службу и повторно чтобы избежать повторного кодирования.
зависимость maven (zipkin_client)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ycg</groupId>
<artifactId>zipkin_client</artifactId>
<version>1.0-SNAPSHOT</version>
<name>zipkin_client</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.1.1.RELEASE</spring-boot.version>
<brave.version>5.6.0</brave.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-bom</artifactId>
<version>${brave.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!-- zipkin客户端依赖 -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-okhttp3</artifactId>
</dependency>
<!-- 添加记录MVC的类、方法名到span的依赖 -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-webmvc</artifactId>
</dependency>
<!-- 添加brave的httpclient依赖 -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpclient</artifactId>
</dependency>
<!-- 集成Brave上下文的log -->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-slf4j</artifactId>
</dependency>
</dependencies>
</project>
Написание класса конфигурации (zipkin_client)
package com.ycg.zipkin_client;
import brave.CurrentSpanCustomizer;
import brave.SpanCustomizer;
import brave.Tracing;
import brave.context.slf4j.MDCScopeDecorator;
import brave.http.HttpTracing;
import brave.httpclient.TracingHttpClientBuilder;
import brave.propagation.B3Propagation;
import brave.propagation.ExtraFieldPropagation;
import brave.propagation.ThreadLocalCurrentTraceContext;
import brave.servlet.TracingFilter;
import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import zipkin2.Span;
import zipkin2.reporter.AsyncReporter;
import zipkin2.reporter.Sender;
import zipkin2.reporter.okhttp3.OkHttpSender;
import javax.servlet.Filter;
/**
* 针对mvc controller 和 restTemplate 的 zipkin客户端配置
*/
@Configuration
@Import(SpanCustomizingAsyncHandlerInterceptor.class)
public class ZipkinClientConfiguration implements WebMvcConfigurer {
/**
* 配置如何向 zipkin 发送 span
*/
@Bean
Sender sender() {
// 注意这里更换为自己安装的zipkin所在的主机IP
return OkHttpSender.create("http://10.150.27.36:9411/api/v2/spans");
}
/**
* 配置如何把 span 缓冲到给 zipkin 的消息
*/
@Bean
AsyncReporter<Span> spanReporter() {
return AsyncReporter.create(sender());
}
/**
* 配置跟踪过程中的Trace信息
*/
@Bean
Tracing tracing(@Value("${spring.application.name}") String serviceName) {
return Tracing.newBuilder()
.localServiceName(serviceName) // 设置节点名称
.propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "user-name"))
.currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
.addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs
.build()
)
.spanReporter(spanReporter()).build();
}
/** 注入可定制的Span */
@Bean
SpanCustomizer spanCustomizer(Tracing tracing) {
return CurrentSpanCustomizer.create(tracing);
}
/** 决定如何命名和标记span。 默认情况下,它们的名称与http方法相同 */
@Bean
HttpTracing httpTracing(Tracing tracing) {
return HttpTracing.create(tracing);
}
/** 导入过滤器,该过滤器中会为http请求创建span */
@Bean
Filter tracingFilter(HttpTracing httpTracing) {
return TracingFilter.create(httpTracing);
}
/**
* 导入 zipkin 定制的 RestTemplateCustomizer
*/
@Bean
RestTemplateCustomizer useTracedHttpClient(HttpTracing httpTracing) {
final CloseableHttpClient httpClient = TracingHttpClientBuilder.create(httpTracing).build();
return new RestTemplateCustomizer() {
@Override public void customize(RestTemplate restTemplate) {
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
};
}
@Autowired
SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;
/** 使用应用程序定义的Web标记装饰服务器span */
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webMvcTracingCustomizer);
}
}
загрузочный служебный модуль
- maven-зависимости: зависимость boot+web start, а также ввести зависимость модуля zipkin_client, упакованную выше.
<dependency>
<groupId>com.ycg</groupId>
<artifactId>zipkin_client</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
- Импорт стартового классаКласс конфигурации для модуля zipkin_clientZipkinClientConfiguration.
@SpringBootApplication
@Import(ZipkinClientConfiguration.class)
public class Service1Application {
public static void main(String[] args) {
SpringApplication.run(Service1Application.class, args);
}
}
- Код для написания Controller, service2 и service3 аналогичен. Поскольку класс конфигурации zipkin внедряет настроенный zipkin RestTemplateCustomizer в контейнер IOC, обратите внимание здесьСоздайте restTemplate с внедренным RestTemplateBuilder.
@EnableAutoConfiguration
@RestController
public class Service1Controller {
private RestTemplate restTemplate;
@Autowired Service1Controller(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
@GetMapping(value = "/service1")
public String getService() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "service1 sleep 100ms ->" + restTemplate.getForObject("http://localhost:8882/service2",String.class);
}
}
- Установите встроенные номера портов tomcat трех загрузочных служб на 8881, 8882 и 8883 соответственно.
Начать проверку
На этом простая демонстрация встроенного в Springboot zipkin завершена.После запуска трех загрузочных приложений соответственно, посетите http://localhost:8881/service1 в браузере, и браузер отобразит следующий рисунок:
Откройте zipkin-webUI, щелкните запрос, и вы сможете найти только что запрошенную ссылку для отслеживания, как показано ниже.
Продолжайте щелкать информацию о найденной ссылке, чтобы просмотреть подробную информацию о отслеживаемой ссылке. Вся цепочка вызовов показана здесь в виде отступа, а также показано время, проведенное после каждого звонка. Нажмите кнопку json в правом верхнем углу, чтобы увидеть данные json этой трассировки.
Подробное объяснение структуры данных span
Обзор структуры JSON и значение каждого поля
Ссылка трассировки будет содержать много интервалов, поэтому трассировка — это массив, и его стандартная структура json выглядит следующим образом:
[
{
"traceId": "string", // 追踪链路ID
"name": "string", // span名称,一般为方法名称
"parentId": "string", // 调用者ID
"id": "string", // spanID
"kind": "CLIENT", // 替代zipkin v1的注解中的四个核心状态,详细介绍见下文
"timestamp": 0, // 时间戳,调用时间
"duration": 0, // 持续时间-调用的服务所消耗的时间
"debug": true,
"shared": true,
"localEndpoint": { // 本地网络节点上下文
"serviceName": "string",
"ipv4": "string",
"ipv6": "string",
"port": 0
},
"remoteEndpoint": { // 远端网络节点上下文
"serviceName": "string",
"ipv4": "string",
"ipv6": "string",
"port": 0
},
"annotations": [ // value通常是缩写代码,对应的时间戳表示代码标记事件的时间
{
"timestamp": 0,
"value": "string"
}
],
"tags": { // span的上下文信息,比如:http.method、http.path
"additionalProp1": "string",
"additionalProp2": "string",
"additionalProp3": "string"
}
}
]
Поговорим об отношениях прошлой жизни между аннотацией и видом.
Аннотация zipkin V1В версии 1 аннотация используется для записи события, событие идентифицируется по значению, и при возникновении события записывается соответствующая метка времени. Некоторые основные аннотации Основные аннотации используются для определения начала и конца запроса. Есть четыре основных аннотации:
- cs — Client Send, указывающий, что клиент инициирует запрос.
- sr — Получение сервером, указывающее, что сервер получает запрос. Вычтите время cs из времени sr, чтобы получить время передачи по сети.
- ss — Server Send, указывающий, что сервер завершает обработку и отправляет результат клиенту. Время использования ss минус время sr — это время, за которое сервер обрабатывает запрос.
- cr — Клиент получен, указывает, что клиент получает информацию, возвращенную сервером. Время, затраченное на использование cr, минус время, затраченное на cs, — это время, затраченное на весь запрос.
Вид zipkin V2V2 использует Span.Kind для замены нескольких основных аннотаций V1, которые указывают начало и конец запроса. Существует четыре типа состояний, и когда они находятся в разных состояниях, метка времени, продолжительность и удаленная конечная точка имеют разные значения.
- КЛИЕНТ:
timestamp — это момент отправки запроса, что эквивалентно аннотации cs в v1.
продолжительность представляет продолжительность после отправки запроса и до получения ответа от сервера, то есть время, затраченное на весь запрос.
remoteEndpoint представляет информацию о сетевом узле вызываемой стороны. - СЕРВЕР:
timestamp — это время, когда сервер получает запрос и готов начать его обработку, что эквивалентно sr в v1.
продолжительность представляет продолжительность после того, как сервер получает запрос и до отправки ответа, то есть чистое время обработки сервера.
remoteEndpoint представляет информацию о сетевом узле вызывающей стороны. - Режиссер:
метка времени — это момент, когда сообщение было отправлено.
продолжительность представляет собой время задержки между окончанием очереди сообщений и сообщением после его отправки отправителем, например, в сценарии пакетной обработки.
remoteEndpoint указывает информацию о сетевом узле очереди сообщений. - ПОТРЕБИТЕЛЬ:
метка времени — это момент, когда сообщение было получено очередью сообщений.
Продолжительность представляет продолжительность сообщения, полученного очередью сообщений и использованного потребителем, например, в сценарии невыполненных сообщений.
remoteEndpoint указывает информацию об узле-потребителе, а unknown указывает имя службы.
V1 также имеет ms, mr и другие аннотации для очередей сообщений, которые здесь подробно описываться не будут. Xiaobai считает, что после замены вида вся ссылка для отслеживания стала более четкой и интуитивно понятной, возможно, это одно из соображений zipkin.
Посмотрите на данные JSON ссылки отслеживания в демоверсии.
Я считаю, что когда вы видите здесь малых партнеров и оглядываетесь назад на данные json ссылки в демонстрации, вы сможете понять конкретное значение. Xiaobai снова разберется здесь. Данные JSON ссылки отслеживания выглядят следующим образом (рекомендуется пропустить данные и посмотреть анализ ниже):
[
{
"traceId": "3857b4a56c99e9f8",
"parentId": "7dd11a047eb02622",
"id": "e5427222edb62a7c",
"kind": "SERVER",
"name": "get /service3",
"timestamp": 1547458424863333,
"duration": 409599,
"localEndpoint": {
"serviceName": "server3",
"ipv4": "172.30.22.138"
},
"remoteEndpoint": {
"ipv4": "127.0.0.1",
"port": 52845
},
"tags": {
"http.method": "GET",
"http.path": "/service3",
"mvc.controller.class": "Service3Controller",
"mvc.controller.method": "getService"
},
"shared": true
},
{
"traceId": "3857b4a56c99e9f8",
"parentId": "7dd11a047eb02622",
"id": "e5427222edb62a7c",
"kind": "CLIENT",
"name": "get",
"timestamp": 1547458424756985,
"duration": 520649,
"localEndpoint": {
"serviceName": "server2",
"ipv4": "172.30.22.138"
},
"tags": {
"http.method": "GET",
"http.path": "/service3"
}
},
{
"traceId": "3857b4a56c99e9f8",
"parentId": "3857b4a56c99e9f8",
"id": "7dd11a047eb02622",
"kind": "SERVER",
"name": "get /service2",
"timestamp": 1547458424446556,
"duration": 880044,
"localEndpoint": {
"serviceName": "server2",
"ipv4": "172.30.22.138"
},
"remoteEndpoint": {
"ipv4": "127.0.0.1",
"port": 52844
},
"tags": {
"http.method": "GET",
"http.path": "/service2",
"mvc.controller.class": "Service2Controller",
"mvc.controller.method": "getService"
},
"shared": true
},
{
"traceId": "3857b4a56c99e9f8",
"parentId": "3857b4a56c99e9f8",
"id": "7dd11a047eb02622",
"kind": "CLIENT",
"name": "get",
"timestamp": 1547458424271786,
"duration": 1066836,
"localEndpoint": {
"serviceName": "server1",
"ipv4": "172.30.22.138"
},
"tags": {
"http.method": "GET",
"http.path": "/service2"
}
},
{
"traceId": "3857b4a56c99e9f8",
"id": "3857b4a56c99e9f8",
"kind": "SERVER",
"name": "get /service1",
"timestamp": 1547458424017344,
"duration": 1358590,
"localEndpoint": {
"serviceName": "server1",
"ipv4": "172.30.22.138"
},
"remoteEndpoint": {
"ipv6": "::1",
"port": 52841
},
"tags": {
"http.method": "GET",
"http.path": "/service1",
"mvc.controller.class": "Service1Controller",
"mvc.controller.method": "getService"
}
}
]
Смотрим на него снизу вверх, и здесь начинается запрос. Сначала посмотрите на нижний пролет (3857b4a56c99e9f8). просить(http://localhost:8881) отправляется браузером, то, когда запрос достигает службы 1, сервер генерирует диапазон типа SERVER, где продолжительность — это время обработки в сети после того, как запрос достигает серверной части, а localEndpoint — информация об узле server1. вызывающая сторона remoteEndpoint также является информацией об узле браузера.
Затем службе 1 необходимо вызвать службу службы 2. В это время служба 1 делает запрос как клиент. Следовательно, будет записан второй спан (7dd11a047eb02622) снизу вверх, клиентский спан, то есть вид=КЛИЕНТ. LocalEndpoint по-прежнему остается самим собой, а информация об отправленном запросе добавляется в тег Продолжительность указывает время, которое требуется для получения ответа от server2 после отправки запроса для /service2. Восходящий диапазон (7dd11a047eb02622) — это диапазон SERVER, записанный сервером 2 после получения запроса от сервера 1. В остальном по той же причине Сяобай много говорить не будет.
заключительные замечания
Здесь Xiaobai представил основные принципы и реализацию распределенной системы отслеживания на основе zipkin.Конечно, это только введение.Собирается ли информация отслеживания полностью или выборочно, какую частоту выборки установить и использовать ли http или kafka для асинхронной отправки span. Эти проблемы необходимо всесторонне рассмотреть в производственной среде на основе реальных сценариев. Что касается этой статьи, Сяобай считает, что если вы внимательно прочитаете ее и обдумаете, вы определенно многое выиграете, за исключением тех, кто изучал ее глубоко. В последующем Xiaobai углубится в исходный код Brave, чтобы понять конкретную реализацию отслеживания.Если есть какая-либо ошибка, пожалуйста, сообщите больше. К тому же рисовать картинки, кодовые слова, разбирать знания непросто.Если вы хотите перепечатать, пожалуйста, укажите источник.