【教程】如何做apm客戶端(關鍵字:Zipkin、Jaeger、SkyWalking、OpenTracing)

小姐姐味道發表於2019-05-10

隨著微服務架構的流行,一次請求往往需要涉及到多個服務,因此服務效能監控和排查就變得更復雜。APM因此而生。

目前,市面上的APM服務端已經有了非常多的實現。比如Zipkin、Jaeger、SkyWalking、Elastic APM等(Pinpoint並不支援OpenTracing,所以我們不介紹)。

本教程僅提供一些開發思路,對技術要求較高,因為大部分需要涉及到對相應元件原始碼的理解。全部原始碼都可以在github上找到。

本次選擇Jaeger作為服務端示例。主要內容如下。


第一部分

在第一部分,我們來看一下apm的關鍵資訊和一些概念性的東西,為後面的示例做準備。在這裡,我們簡單的描述了jaeger是如何安裝的,也是一些準備工作。

第二部分

使用一個簡單的java程式,來說明如何使用OpenTracing api進行trace的生成,並演示一個帶有二個節點的呼叫鏈生成。

第三部分

使用OkHttp3和SpringBoot,來演示如何實現一個分散式呼叫。本文依然是使用底層的api進行構建,對開發感覺好的到此應該能夠應對各種場景。

第四部分

以SpringCloud為例,說明微服務的呼叫鏈生成邏輯。由於各種呼叫內容較多,我們僅以比較流行的Feign呼叫來說明其原理。 SpringCloud的實現已經有了比較好的輪子,考慮的也比較全面,可以參考其原始碼進行分析。


如果你在開發自己的中介軟體,或者做一些整合性的工作,本教程能夠讓你快速給自己的元件加入apm功能。前提是,你的api相容OpenTracing協議。


整體介紹

對於監控體系和apm鏈路,小姐姐味道公眾號有更加詳細的描述,建議先讀一下。

這麼多監控元件,總有一款適合你

以下僅簡要說明一下呼叫鏈的主要因素。

呼叫鏈主要因素

資料收集部分

主要用於多樣化的資料收集,為資料分析做準備。要求易用好用侵入儘量小(開發工作量),並且在極端情況下(如收集元件不可用)不能對業務有任何影響。可以看到此部分的開發量是巨大的,尤其是需要整合Nginx上下游、基礎元件多樣、技術棧多樣的情況下。

資料分析部分

主要有實時分析與線下分析。一般,實時分析的價值更大一些,主要產出如秒級別的呼叫量、平均響應時間、TP值等。另外,呼叫鏈(Trace)需要儲存全量資料,一些高併發大埋點的請求,會有效能問題。

監控報警

此部分利用資料分析的產出,通過簡訊郵件等形式,通知訂閱人關注。監控報警平臺應儘量向devops平臺靠攏,包括自主化服務平臺。

為什麼選用jaeger

jaeger的開發較為活躍,而且它的模組劃分是比較靈活的。在資料量非常大的情況下,資料是可以先用kafka緩衝一下的(同時為接入各種流分析平臺做足了準備)。這些內容,我們在jaeger安裝的部分順便說明。

擴充套件方式

如果你的專案使用了SpringBoot,是非常方便進行擴充套件的。以下內容不止一次提起,讀過的請忽略。

我們接下來實現的功能是:任何加了 @OwlTrace註解的方法,都將產生一條呼叫鏈資訊。

首先,我們要定義一個註解:

import java.lang.annotation.*;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OwlTrace {
}
複製程式碼

然後,實現其處理類。程式碼通過AOP對Spring管理的Bean進行攔截,非常簡單的實現了Trace資訊的構造。程式碼如下:

import io.opentracing.Span;
import io.opentracing.Tracer;
import io.opentracing.tag.Tags;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import java.util.LinkedHashMap;
import java.util.Map;


@Configuration
@Slf4j
public class OwlTraceAutoConfiguration {
    static final String TAG_COMPONENT = "java";

    @Autowired
    @Lazy
    Tracer tracer;

    @Bean
    public TracingAspect pxTracingAspect() {
        return new TracingAspect();
    }

    @Aspect
    class TracingAspect {
        @Around("@annotation(com.sayhiai.arch.trace.annotation.OwlTrace)")
        public Object pxTraceProcess(ProceedingJoinPoint pjp) throws Throwable {
            Span span = null;
            if (tracer != null) {
                final String cls = pjp.getTarget().getClass().getName();
                final String mName = pjp.getSignature().getName();
                span = tracer.buildSpan(cls + "." + mName)
                        .withTag(Tags.COMPONENT.getKey(), TAG_COMPONENT)
                        .withTag("class", cls)
                        .withTag("method", mName)
                        .startActive(false)
                        .span();
            }
            try {
                return pjp.proceed();
            } catch (Throwable t) {
                Map<String, Object> exceptionLogs = new LinkedHashMap<>(2);
                exceptionLogs.put("event", Tags.ERROR.getKey());
                exceptionLogs.put("error.object", t);
                span.log(exceptionLogs);
                Tags.ERROR.set(span, true);
                throw t;
            } finally {
                if (tracer != null && span != null) {
                    span.finish();
                }
            }
        }
    }
}

複製程式碼

最後,根據Spring的載入方式,將路徑新增到src/main/resources/META-INF/spring.factories中:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
arch.trace.core.owl.OwlTraceAutoConfiguration,\
arch.trace.core.log.LoggingAutoConfiguration
複製程式碼

將元件打成jar包,一個spring boot starter就實現了。

使用

application.properties

確保開了AOP

# AOP
spring.aop.auto=true
spring.aop.proxy-target-class=true

opentracing.jaeger.log-spans=true
opentracing.jaeger.udp-sender.host=192.168.3.237
opentracing.jaeger.udp-sender.port=5775
複製程式碼

End

接下來,我們將逐步進入客戶端api的開發之中。

【教程】如何做apm客戶端(關鍵字:Zipkin、Jaeger、SkyWalking、OpenTracing)

相關文章