【SpringCloud構建微服務系列】分佈式鏈路跟蹤Spring Cloud Sleuth

1、背景

隨着業務的發展,系統規模愈來愈大,各微服務直接的調用關係也變得愈來愈複雜。一般一個由客戶端發起的請求在後端系統中會通過多個不一樣的微服務調用協同產生最後的請求結果,幾乎每個前端請求都會造成一條複雜的分佈式服務調用鏈路,對每一個請求實現全鏈路跟蹤,能夠幫助咱們快速發現錯誤根源以及監控分析每條請求鏈路上性能瓶頸。
針對分佈式服務跟蹤,Spring Cloud Sleuth提供了一套完整的解決方案。html

2、原理

參考《SpringCloud微服務實戰》第11章。
先查看跟蹤日誌瞭解每項的含義前端

2019-02-27 20:40:56.419  INFO [service-a,e79b41414a56743d,e79b41414a56743d,true] 8276 --- [nio-9001-exec-5] cn.sp.controller.ServiceController       : ==<Call Service-a>==
  1. 第一個值service-a:記錄了應用的名稱,也就是spring.application.name的值。
  2. 第二個值e79b41414a56743d:SpringCloudSleuth生成的一個ID,叫TraceID,它用來標識一條請求鏈路。一條請求鏈路中包含一個TraceID,多個 SpanID。
  3. 第三個值e79b41414a56743d,SpringCloudSleuth生成的另一個ID,叫SpanID,它表示一個基本的工做單元,好比發送一個HTTP請求。
  4. 第四個值true:表示是否要將該信息輸出到Zipkin等服務中收集和展現。

3、編碼

3.1建立一個Eureka註冊中心

這個以前的文章有,故省略java

3.2調用者service-a

1.建立項目引入依賴git

  1. <dependency> 
  2. <groupId>org.springframework.boot</groupId> 
  3. <artifactId>spring-boot-starter-web</artifactId> 
  4. </dependency> 
  5. <dependency> 
  6. <groupId>org.springframework.cloud</groupId> 
  7. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 
  8. </dependency> 
  9. <dependency> 
  10. <groupId>org.springframework.cloud</groupId> 
  11. <artifactId>spring-cloud-starter-sleuth</artifactId> 
  12. </dependency> 
  13. <dependency> 
  14. <groupId>org.springframework.cloud</groupId> 
  15. <artifactId>spring-cloud-starter-zipkin</artifactId> 
  16. </dependency> 

其實spring-cloud-starter-zipkin中已經有了sleuth的依賴,spring-cloud-starter-sleuth能夠省略。
2. 啓動類github

@EnableEurekaClient
@SpringBootApplication
public class ServiceAApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceAApplication.class, args);
    }

    @Bean
    @LoadBalanced
    RestTemplate restTemplate(){
        return new RestTemplate();
    }
}
  1. ServiceController
@RestController
public class ServiceController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());


    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/service-a")
    public String test(){
        logger.info("==<Call Service-a>==");
        return restTemplate.getForEntity("http://service-b/test",String.class).getBody();
    }
}

這裏的url(http://service-b/test)用的是服務名當虛擬域名而不是ip+端口號的形式,至於爲何能夠訪問能夠查看這篇文章【Spring Cloud中restTemplate是如何經過服務名主求到具體服務的】。web

4.配置文件application.ymlspring

  1. spring: 
  2. application: 
  3. name: service-a 
  4. zipkin: 
  5. base-url: http://localhost:9411 
  6. # 抽樣收集的百分比,默認10% 
  7. sleuth: 
  8. sampler: 
  9. probability: 1 
  10. eureka: 
  11. client: 
  12. serviceUrl: 
  13. defaultZone: http://localhost:8761/eureka/ 
  14. server: 
  15. port: 9001 

3.3被調用者service-b

1.建立項目引入依賴pom文件同上
2.啓動類添加註解 @EnableEurekaClient
3.ServiceController後端

@RestController
public class ServiceController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @GetMapping("/test")
    public String test(){
        logger.info("==<Call Service-b>==");
        return "OK";
    }
}

4.配置文件application.ymlbash

  1. spring: 
  2. application: 
  3. name: service-b 
  4. zipkin: 
  5. base-url: http://localhost:9411 
  6. # 抽樣收集的百分比,默認10% 
  7. sleuth: 
  8. sampler: 
  9. probability: 1 
  10. eureka: 
  11. client: 
  12. serviceUrl: 
  13. defaultZone: http://localhost:8761/eureka/ 
  14. server: 
  15. port: 9002 

3.4建立zipkin-server

  1. pom.xml
  1. <dependency> 
  2. <groupId>io.zipkin.java</groupId> 
  3. <artifactId>zipkin-server</artifactId> 
  4. <version>2.12.2</version> 
  5. <exclusions> 
  6. <exclusion> 
  7. <groupId>org.springframework.boot</groupId> 
  8. <artifactId>spring-boot-starter-log4j2</artifactId> 
  9. </exclusion> 
  10. </exclusions> 
  11. </dependency> 
  12. <!-- https://mvnrepository.com/artifact/io.zipkin.java/zipkin-autoconfigure-ui --> 
  13. <dependency> 
  14. <groupId>io.zipkin.java</groupId> 
  15. <artifactId>zipkin-autoconfigure-ui</artifactId> 
  16. <version>2.12.2</version> 
  17. </dependency> 

2.啓動類添加註解 @EnableZipkinServer
注意: 這裏我排除了log4j的依賴,由於已經有了相同的類文件啓動會報錯。微信

4、測試

  1. 依次啓動eureka註冊中,service-a,service-b,zipkin-server
  2. 訪問http://localhost:8761/看到有兩個註冊實例,說明啓動成功。
    enter description here
  3. 遊覽器訪問http://localhost:9001/service-a請求服務A
    若是報錯日誌打印java.lang.IllegalStateException: No instances available for service-b,那多是服務B還沒註冊完成等會兒再試下便可。
  4. 正常狀況會看到遊覽器顯示 OK
    服務A的日誌以下:
2019-02-27 21:18:05.119  INFO [service-a,3e487761c9b13a2e,3e487761c9b13a2e,true] 8276 --- [nio-9001-exec-9] cn.sp.controller.ServiceController       : ==<Call Service-a>==

服務B的日誌以下:

2019-02-27 21:18:05.128  INFO [service-b,3e487761c9b13a2e,8d49352c6645c6f9,true] 13860 --- [nio-9002-exec-8] cn.sp.controller.ServiceController       : ==<Call Service-b>==
  1. 訪問http://localhost:9411/zipkin/進入可視化界面
    enter description here

點擊查找,服務名選擇service-a便可看到最近的統計數據,還能夠根據條件進行篩選。

點擊依賴,依賴分析就能夠看到服務以前的依賴關係。
enter description here
完整代碼地址。

5、總結

基本是根據《SpringCloud微服務實戰》這本書來的,可是中間仍是踩了幾個坑,好比開始報No instances available for service-b的時候我一直覺得是代碼哪裏出了問題,浪費了不少時間。

SpringCloudSleuth除了把請求鏈路數據整合到Zipkin中外,還能夠保存到消息隊列,ELK等,還算比較強大。可是除了HTTP請求,不知道對於GRPC這樣的通訊方式是否也能實現鏈路跟蹤。

查了下資料支持Grpc的,demo地址:https://github.com/tkvangorder/sleuth-grpc-sample

官方文檔:https://cloud.spring.io/spring-cloud-sleuth/single/spring-cloud-sleuth.html#_grpc

相關文章
相關標籤/搜索