天天看點

Spring Cloud Sleuth-Zipkin鍊路追蹤使用說明1.簡介2.術語3.架構4.實戰

1.簡介

        微服務架構是一個分布式結構架構,一般按業務劃分為多個服務單元,服務單元間會通信進行資料互動。由于服務單元數量單元多,業務複雜,如果出現了異常或者錯誤,很難去定位。是以微服務架構中,必須實作分布式鍊路追蹤,去跟進一個請求到底有哪些服務參與,參與的順序又是怎樣的,進而達到每個請求的步驟清晰可見,出了問題,很快定位。

        Spring Cloud Sleuth則實作了分布式跟蹤解決方案。內建Zipkin進行日志鍊路檢視,非常友善。

        官網位址:https://cloud.spring.io/spring-cloud-sleuth

2.術語

        Spring Cloud Sleuth借鑒了Dapper的術語。

Span:基本工作機關。例如,發送RPC是一個新的跨度,就像發送響應到RPC一樣。跨度由跨度的唯一64位ID和跨度所屬的跟蹤的另一個64位ID辨別。跨區還具有其他資料,例如描述,帶有時間戳的事件,鍵值注釋(标簽),引起跨度的跨區的ID和程序ID(通常為IP位址)。

        跨度可以啟動和停止,并且可以跟蹤其時序資訊。建立跨度後,您必須在将來的某個時間點将其停止。

Trace:一組形成樹狀結構的跨度。例如,如果您運作分布式大資料存儲,則跟蹤可能是由PUT請求形成的。

Annotation:用來及時記錄一個事件的,一些核心注解用來定義一個請求的開始和結束 。這些注解包括以下:

cs:Client Sent -用戶端發送一個請求,這個注解描述了這個Span的開始

sr:Server Received -服務端獲得請求并準備開始處理它,如果将其sr減去cs時間戳便可得到網絡傳輸的時間。

ss:Server Sent (服務端發送響應)–該注解表明請求處理的完成(當請求傳回用戶端),如果ss的時間戳減去sr時間戳,就可以得到伺服器請求的時間。

cr:Client Received (用戶端接收響應)-此時Span的結束,如果cr的時間戳減去cs時間戳便可以得到整個請求所消耗的時間。

3.架構

下圖顯示了Span和Trace以及Zipkin在系統中的追蹤:

Spring Cloud Sleuth-Zipkin鍊路追蹤使用說明1.簡介2.術語3.架構4.實戰

4.實戰

4.1.Zipkin服務端

        從github上git clone項目,本地運作。後續可以直接下載下傳jar運作,可另行百度。

        git位址:https://github.com/openzipkin/zipkin.git

        Zipkin接收spring cloud sleuth傳過來的追蹤資料預設是儲存在記憶體中,直接啟動即可。

        若需要儲存到elasticsearch,需部署啟動elasticsearch後,在zipkin-server中配置如下:

Spring Cloud Sleuth-Zipkin鍊路追蹤使用說明1.簡介2.術語3.架構4.實戰

1.新增配置檔案zipkin-server-param.yml,内容如下:

STORAGE_TYPE: elasticsearch
ES_HOSTS: localhost:9200
           

2.在zipkin-server.yml中增加配置:

spring.profiles.include: shared,param
           

3.重新啟動,則資料儲存至elasticsearch中。

啟動成功後,浏覽器通路http://127.0.0.1:9411即可通路追蹤頁面

Spring Cloud Sleuth-Zipkin鍊路追蹤使用說明1.簡介2.術語3.架構4.實戰

4.2.spring boot服務

4.2.1.建構一個測試的spring boot服務,pom.xml中引用依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
           

4.2.2配置檔案application.yml中内容分如下

spring:
  sleuth:
    enabled: true
    trace-id128: true
    sampler:
      probability: 1.0
  application:
    name: teseService

  zipkin:
    service:
      name: myService
#  zipkin:
#    base-url: http://localhost:9411
logging:
  level:
    org.springframework.web: debug
server:
  port: 8080
           

4.2.3主程式類

@SpringBootApplication
@ComponentScan(basePackages = {"com.sky"})
@EnableScheduling
public class ServicecombZipkinApplication {
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    /**
     * 提取器 優先高于TagValueExpressionResolver
     * @return
     */
    @Bean(name = "myCustomTagValueResolver")
    public TagValueResolver TagValueResolver(){
        return parameter -> {
            if (parameter == null) {
                return null;
            }
            if(parameter instanceof List){
                return String.format("list's size is %d",((List)parameter).size());
            }
            if(parameter instanceof Map){
                return String.format("map's size is %d",((Map)parameter).size());
            }
            return (String)parameter;
        };
    }
    /**
     * tag 值解析表達式
     * @return
     */
    @Bean
    public TagValueExpressionResolver tagValueExpressionResolver(){
        return (expression, parameter) -> {
            System.out.println(expression);
            System.out.println(parameter);
            return expression;
        };
    }
    @Autowired
    private BeanFactory beanFactory;
    @Bean("lazyTraceExecutor")
    public Executor getAsyncExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(10);
        executor.setThreadNamePrefix("my-executor-");
        executor.initialize();
        return new LazyTraceExecutor(this.beanFactory,executor);
    }
    @Bean("traceableExecutorService")
    public TraceableExecutorService traceableExecutorService(){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,50,1000, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));
        return new TraceableExecutorService(this.beanFactory, threadPoolExecutor);
    }
    public static void main(String[] args) {
        SpringApplication.run(ServicecombZipkinApplication.class, args);
    }

}
           

注意:Spring cloud sleuth要追蹤日志,需要時spring管理的bean才被追蹤到,是以使用過程中,要被追蹤的操作需要注冊bean到spring中。

4.2.4建立一個TraceService.java,負責追蹤的一些操作全封裝在這個bean中

import brave.Span;
import brave.Tracer;
import brave.propagation.ExtraFieldPropagation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class TracerService {
    @Autowired(required = false)
    private Tracer tracer;
    public Span currentSpan() {
        if (tracer == null) {
            return null;
        }
        return tracer.currentSpan();
    }

    public void addTagToCurrentSpan(String key, String value) {
        Span span = this.currentSpan();
        if (span == null) {
            return;
        }
//        span.tag("baz",
//                ExtraFieldPropagation.get(span.context(), "foo"));
        span.tag(key, value);
    }

    public void addToContext(String key,String value){
        Span span = this.currentSpan();
        if(span == null){
            return;
        }
        ExtraFieldPropagation.set(span.context(), key, value);
    }

    public Tracer tracer(){
        return tracer;
    }
}
           

注意:@Autowired(required = false) private Tracer tracer;

以免關閉功能時spring.sleuth.enabled=false bean注入報錯。

4.2.5測試服務TestService.java

@Component
public class TestService {
    @Autowired
    TestService tranceService;
    @Autowired
    TracerService tracer;
    @Autowired
    Util util;
    @Qualifier("lazyTraceExecutor")
    @Autowired
    Executor executor;

    @NewSpan(name = "服務測試")
    public void test(@SpanTag(key = "tag", expression = "'hello' + tag + ' characters'") String s) {
        System.out.println("------------------------------->test");
        tracer.addToContext("baz", "父類的foo");
        executor.execute(() -> System.out.println("asd---------"));
        executor.execute(() -> tranceService.thread("thread----"));
        tranceService.test1("s");
    }

    @NewSpan("asyncSpan")
    public void thread(@SpanTag(key = "param") String param) {
        System.out.println("thread----->" + param);
        this.tranceService.test1("oo");
    }

    @NewSpan(name = "服務測試1")
//    @SpanName("服務測試2")
//    @ContinueSpan(log = "aa")
    public void test1(@SpanTag(key = "s", expression = "'hello' + ' characters'", resolver = TagValueResolver.class) String s) {
        util.test("cesh");
        System.out.println("-------------------------->test1" + s);
        tracer.addTagToCurrentSpan("success", "成功l");
        throw new NullPointerException("空指針。。。");
    }

    @Scheduled(cron = "0/15 * * * * *")
    @NewSpan("測試定時器")
    public void schedule() {
        tranceService.test("scheme");
    }
}
           

調用本類中的方法時,需在本類中注入@Autowired TestService tranceService;

在通過tranceService.test("");這樣,才會被追蹤到span中。

支援@Scheduled方式的定時器、LazyTraceExecutor方式的線程池、bean方法調用資訊追蹤。

@NewSpan是在tracer中建立一個span,@ContinueSpan是沿用本span。

追蹤資訊會異步傳輸到zipkin服務端儲存,直接在zipkin-ui中檢視追蹤資訊。

4.3.Zipkin展示

Spring Cloud Sleuth-Zipkin鍊路追蹤使用說明1.簡介2.術語3.架構4.實戰
Spring Cloud Sleuth-Zipkin鍊路追蹤使用說明1.簡介2.術語3.架構4.實戰
Spring Cloud Sleuth-Zipkin鍊路追蹤使用說明1.簡介2.術語3.架構4.實戰

繼續閱讀