Знакомство с распределенной системой трассировки zipkin

задняя часть Микросервисы MySQL сервер

1 Обзор

Zipkin — это полнофункциональная система мониторинга ссылок, разработанная на основе статьи Google Dapper и разработанная Twitter.

Zipkin использует структуру Trace для представления трассировки запроса и делит каждую трассировку на несколько интервалов с зависимостями. В микросервисной архитектуре пользовательский запрос может обрабатываться несколькими службами в фоновом режиме, тогда каждая служба, обрабатывающая запрос, может пониматься как Span (который может включать службы API, службы кэширования, службы базы данных, службы отчетов и т. д.). . Конечно, эта служба может продолжать запрашивать другие службы, поэтому Span представляет собой древовидную структуру, отражающую отношения вызова между службами.

2. Схема архитектуры

zipkin架构图

Пунктирная линия — это функция, предоставляемая zipkin-сервером, который в основном включает в себя четыре модуля:

  • Collector принимает данные, переданные клиентами, собирающими zipkin.
  • Хранилище хранит собранные данные, по умолчанию это Память, которую можно настроить как MySQL, Cassandra, ES и т.д.
  • API отвечает за запрос данных, хранящихся в хранилище, и предоставление их пользовательскому интерфейсу.
  • Пользовательский интерфейс предоставляет простые веб-страницы.

Instrumented — это клиент, который собирает данные и отправляет их в zipkin. Основными собираемыми структурами данных являются Trace и Span.

3.Brave

Brave — это клиент zipkin для Java.

Обычно мы не пишем код, связанный с трассировкой, вручную, Brave предоставляет несколько готовых библиотек, которые помогают нам отслеживать некоторые конкретные запросы. Такие как dubbo, grpc, servlet, mysql, httpClient, kafka, springMVC и т. д.

    /**
     *  客户端具体怎么收集数据的demo
     */
    public static void main(String[] args) throws Exception{
        // 1.构建客户端发送工具
        Sender sender = OkHttpSender.create("http://127.0.0.1:9411/api/v2/spans");
        // 2.构建异步reporter
        AsyncReporter asyncReporter = AsyncReporter.builder(sender)
                .closeTimeout(500, TimeUnit.MILLISECONDS)
                .build(SpanBytesEncoder.JSON_V2);
        // 3.构建tracing上下文
        Tracing tracing = Tracing.newBuilder()
                .localServiceName("myService")
                .spanReporter(asyncReporter)
                .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY,"shuang"))
                .currentTraceContext(ThreadContextCurrentTraceContext.create())
                .build();
        // 4.使用tracer创建span并操作start()和finish()方法
        Tracer tracer = tracing.tracer();
        Span span = tracer.newTrace().name("total").start();

        Span action_1 = tracer.newChild(span.context()).name("action-1").start();
        try {
            Thread.sleep(500);
        }finally {
            action_1.finish();
        }

        Span action_2 = tracer.newChild(span.context()).name("action-2").start();
        try {
            Thread.sleep(500);
        }finally {
            action_2.finish();
        }

        try {
            Thread.sleep(2000);
        }finally {
            span.finish();
        }
    }

3. Введение в трассировку

Несколько важных классов зависимостей в Tracing

  • IP-адрес конечной точки, порт, имя службы приложений и другая информация.
  • Reporter Сбор данных Reporter
  • Пробоотборник, определяющий необходимость отбора проб трассировки в соответствии с TRACEID
  • CurrentTraceContext привязывает TraceContext к ThreadLocal, чтобы текущий поток мог получить
  • Распространение — это интерфейс, который может вводить и извлекать данные в объектный носитель данных и из него.

3.1Sampler

public abstract class Sampler {
    /**
     *  根据traceId是否需要采样
     */
    public abstract boolean isSampled(long traceId);
}

Он предоставляет несколько реализаций сэмплера по умолчанию. ALWAYS_SAMPLE, NERVER_SAMPLE и сэмплер CountingSampler с определяемой частотой дискретизации.

4. Промежуток Введение

Основными интерфейсами для сбора и отправки данных являются Span.start() и Span.finish().

  /**
   *  RealSpan依赖于Trace上下文,Recorder。
   */
  static RealSpan create(TraceContext context, Clock clock, Recorder recorder) {
    return new AutoValue_RealSpan(context, clock, recorder);
  }
  
  public Span start(long timestamp) {
    recorder().start(context(), timestamp);
    return this;
  }
  
  public void finish(long timestamp) {
    recorder().finish(context(), timestamp);
  }
  
  public void finish(TraceContext context, long finishTimestamp) {
    MutableSpan span = spanMap.remove(context);
    if(span == null || noop.get()) return;
    synchronized (span) {
        span.finsh(finishTimestamp);
        reporter.report(span.toSpan());
    }
  }

RealSpan вызывает start() и finish() для получения информации Span, связанной с TraceContext, записывает время начала и время окончания, а в конце вызывает метод отчета генератора отчетов и сообщает об этом zipkin.

5. Интеграция с SpringMVC

Brave использует механизм перехватчика, предоставляемый springMVC, для вызова предыдущего кода, связанного с диапазоном, до и после перехвата, то есть для мониторинга запроса.

   <!-- Brave提供的拦截器 -->
   <mvc:interceptors>
    <bean class="brave.spring.webmvc.TracingHandlerInterceptor" factory-method="create">
      <constructor-arg type="brave.http.HttpTracing" ref="httpTracing"/>
    </bean>
  </mvc:interceptors>

Вызовите методы span.start() и span.finish() в preHandle() и afterCompletion() соответственно, чтобы завершить отправку отчета на сервер zipkin. Подробности смотрите в соответствующем коде.

6. Интеграция с Mysql

Mysql также предоставляет интерфейс перехвата до и после выполнения sql.

 /**
  * mysql-connector.jar中提供的拦截接口
  */
 public interface StatementInterceptorV2 extends Extension {
    void init(Connection var1, Properties var2) throws SQLException;

    ResultSetInternalMethods preProcess(String var1, Statement var2, Connection var3) throws SQLException;

    boolean executeTopLevelOnly();

    void destroy();

    ResultSetInternalMethods postProcess(String var1, Statement var2, ResultSetInternalMethods var3, Connection var4, int var5, boolean var6, boolean var7, SQLException var8) throws SQLException;
}

Выполните span.start() в preProcess() и span.finish() в postProcess(). Подробности смотрите в соответствующем коде.

7. Другая интеграция промежуточного программного обеспечения опущена.

8. Резюме

Краткое введение в Zipkin. Хороший дизайн фреймворка принесет много удобств разработчикам. (Большинство клиентов zipkin реализованы на основе интерфейса перехвата.)