Zipkin — 微服務鏈路跟蹤.

JMCui發表於2019-05-30

一、Zipkin 介紹

Zipkin 是什麼?

 Zipkin的官方介紹:https://zipkin.apache.org/

 Zipkin是一款開源的分散式實時資料追蹤系統(Distributed Tracking System),基於 Google Dapper的論文設計而來,由 Twitter 公司開發貢獻。其主要功能是聚集來自各個異構系統的實時監控資料。分散式跟蹤系統還有其他比較成熟的實現,例如:Naver的Pinpoint、Apache的HTrace、阿里的鷹眼Tracing、京東的Hydra、新浪的Watchman,美團點評的CAT,skywalking等。

為什麼用 Zipkin?

 隨著業務越來越複雜,系統也隨之進行各種拆分,特別是隨著微服務架構和容器技術的興起,看似簡單的一個應用,後臺可能有幾十個甚至幾百個服務在支撐;一個前端的請求可能需要多次的服務呼叫最後才能完成;當請求變慢或者不可用時,我們無法得知是哪個後臺服務引起的,這時就需要解決如何快速定位服務故障點,Zipkin分散式跟蹤系統就能很好的解決這樣的問題。

Zipkin的一些基本概念?

Brave

Brave 是用來裝備 Java 程式的類庫,提供了面向 Standard Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等介面的裝備能力,可以通過編寫簡單的配置和程式碼,讓基於這些框架構建的應用可以向 Zipkin報告資料。同時 Brave 也提供了非常簡單且標準化的介面,在以上封裝無法滿足要求的時候可以方便擴充套件與定製。

如下圖是 Brave 的結構圖。Brave 利用 reporter 向 Zipkin的 Collector 傳送 trace 資訊。
Zipkin — 微服務鏈路跟蹤.

Brave 主要是利用攔截器在請求前和請求後分別埋點。例如 Spingmvc 監控使用 Interceptors,Mysql 監控使用 statementInterceptors。同理 Dubbo 的監控是利用 com.alibaba.dubbo.rpc.Filter 來過濾生產者和消費者的請求。

traceId

一次請求全域性只有一個traceId。用來在海量的請求中找到同一鏈路的幾次請求。比如servlet伺服器接收到使用者請求,呼叫dubbo服務,然後將結果返回給使用者,整條鏈路只有一個traceId。開始於使用者請求,結束於使用者收到結果。

spanId

一個鏈路中每次請求都會有一個spanId。例如一次rpc,一次sql都會有一個單獨的spanId從屬於traceId。

cs

Clent Sent 客戶端發起請求的時間,比如 dubbo 呼叫端開始執行遠端呼叫之前。

cr

Client Receive 客戶端收到處理完請求的時間。

ss

Server Receive 服務端處理完邏輯的時間。

sr

Server Receive 服務端收到呼叫端請求的時間。

sr - cs = 請求在網路上的耗時
ss - sr = 服務端處理請求的耗時
cr - ss = 回應在網路上的耗時
cr - cs = 一次呼叫的整體耗時

Zipkin的工作過程

 當使用者發起一次呼叫時,Zipkin 的客戶端會在入口處為整條呼叫鏈路生成一個全域性唯一的 trace id,併為這條鏈路中的每一次分散式呼叫生成一個 span id。span 與 span 之間可以有父子巢狀關係,代表分散式呼叫中的上下游關係。span 和 span 之間可以是兄弟關係,代表當前呼叫下的兩次子呼叫。一個 trace 由一組 span 組成,可以看成是由 trace 為根節點,span 為若干個子節點的一棵樹。

 Zipkin 會將 trace 相關的資訊在呼叫鏈路上傳遞,並在每個呼叫邊界結束時非同步的把當前呼叫的耗時資訊上報給 Zipkin Server。Zipkin Server 在收到 trace 資訊後,將其儲存起來。隨後 Zipkin 的 Web UI 會通過 API 訪問的方式從儲存中將 trace 資訊提取出來分析並展示。

Zipkin — 微服務鏈路跟蹤.

二、Zipkin的部署與執行

Zipkin的 github 地址:https://github.com/apache/incubator-zipkin

Docker 方式

docker run -d -p 9411:9411 openzipkin/zipkin

Jar 包方式(JDK8)

curl -sSL https://zipkin.apache.org/quickstart.sh | bash -s
java -jar zipkin.jar

注意:以上方式的 Zipkin 都是基於記憶體儲存,Zipkin 重啟後資料會丟失,建議測試環境使用。Zipkin 支援的儲存型別有 inMemory、MySql、Cassandra、以及 ElasticsSearch 幾種方式。正式環境推薦使用 Cassandra 和 ElasticSearch。

Zipkin — 微服務鏈路跟蹤.

三、Zipkin 與 Dubbo 和 Springmvc 的整合

上面我們搭建好了 Zipkin 伺服器,現在的任務就是如何把我們系統內產生的請求資料包送給 Zipkin 伺服器,以便在 UI 上渲染出來。

1. pom.xml

        <!-- 使用 okhttp3 作為 reporter -->
        <dependency>
            <groupId>io.zipkin.reporter2</groupId>
            <artifactId>zipkin-sender-okhttp3</artifactId>
            <version>2.8.2</version>
        </dependency>
        <!--  brave 對 dubbo 的整合 -->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-dubbo-rpc</artifactId>
            <version>5.6.3</version>
        </dependency>
        <!--  brave 對 mvc 的整合 -->
        <dependency>
            <groupId>io.zipkin.brave</groupId>
            <artifactId>brave-instrumentation-spring-webmvc</artifactId>
            <version>5.6.3</version>
        </dependency>

2. application.yml

zipkin:
  url: http://127.0.0.1:9411/api/v2/spans
  connectTimeout: 5000
  readTimeout: 10000
  # 取樣率,指的是多次請求中有百分之多少傳到zipkin。例如 1.0 是全部取樣,0.5是 50% 取樣
  rate: 1.0f

3. ZipkinProperties.java

@Configuration
@ConfigurationProperties("zipkin")
public class ZipkinProperties {


    @Value("${spring.application.name}")
    private String serviceName;
    private String url;
    private Long connectTimeout;
    private Long readTimeout;
    private Float rate;


    /*getter and setter*/
}

注意:記得在 SpringBoot 的啟動類上加上 @EnableConfigurationProperties 註解才能使 @ConfigurationProperties("zipkin") 生效哦!

4. ZipkinConfig.java

@Configuration
public class ZipkinConfig {

    @Autowired
    private ZipkinProperties zipkinProperties;

    /**
     * 為了實現 dubbo rpc呼叫的攔截
     *
     * @return
     */
    @Bean
    public Tracing tracing() {
        Sender sender = OkHttpSender.create(zipkinProperties.getUrl());
        AsyncReporter reporter = AsyncReporter.builder(sender)
                .closeTimeout(zipkinProperties.getConnectTimeout(), TimeUnit.MILLISECONDS)
                .messageTimeout(zipkinProperties.getReadTimeout(), TimeUnit.MILLISECONDS)
                .build();
        Tracing tracing = Tracing.newBuilder()
                .localServiceName(zipkinProperties.getServiceName())
                .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "shiliew"))
                .sampler(Sampler.create(zipkinProperties.getRate()))
                .spanReporter(reporter)
                .build();
        return tracing;
    }


    /**
     * MVC Filter,為了實現 SpringMvc 呼叫的攔截
     * @param tracing
     * @return
     */
    @Bean
    public Filter tracingFilter(Tracing tracing) {
        HttpTracing httpTracing = HttpTracing.create(tracing);
        httpTracing.toBuilder()
                .serverParser(new HttpServerParser() {
                    @Override
                    public <Req> String spanName(HttpAdapter<Req, ?> adapter, Req req) {
                        return adapter.path(req);
                    }
                })
                .clientParser(new HttpClientParser() {
                    @Override
                    public <Req> String spanName(HttpAdapter<Req, ?> adapter, Req req) {
                        return adapter.path(req);
                    }
                }).build();
        return TracingFilter.create(httpTracing);
    }
}

如此,我們就把 Zipkin 和 Dubbo 以及 Springmvc 的整合做好了:
Zipkin — 微服務鏈路跟蹤.

踩過的坑如下,共勉:

  • Zipkin Server 一定要在呼叫後才會產生資料,不會先把服務的資訊註冊上去。
  • MVC 的攔截,span 的名字是以請求方式命名的,如下:
    Zipkin — 微服務鏈路跟蹤.
  • 如果仍然想檢視,某個請求路徑的呼叫情況呢?
    Zipkin — 微服務鏈路跟蹤.

  • github 原始碼:https://github.com/JMCuixy/dubbo-demo

相關文章