分布式鏈路監控與追蹤系統Zipkin


1.分布式鏈路監控與追蹤產生背景
2.SpringCloud Sleuth + Zipkin
3.分布式服務追蹤實現原理
4.搭建Zipkin服務追蹤系統
5.搭建Zipkin集成RabbitMQ異步傳輸
6.SpringCloud2.x新知識介紹

 

分布式鏈路監控與追蹤產生背景

在微服務系統中,隨着業務的發展,系統會變得越來越大,那么各個服務之間的調用關系也就變得越來越復雜。一個 HTTP 請求會調用多個不同的微服務來處理返回最后的結果,在這個調用過程中,可能會因為某個服務出現網絡延遲過高或發送錯誤導致請求失敗,這個時候,對請求調用的監控就顯得尤為重要了。Spring Cloud Sleuth 提供了分布式服務鏈路監控的解決方案。下面介紹 Spring Cloud Sleuth 整合 Zipkin 的解決方案。

 

Zipkin框架介紹

Zipkin 是 Twitter 的一個開源項目,它基於 Google Dapper 實現的。我們可以使用它來收集各個服務器上請求鏈路的跟蹤數據,並通過它提供的 REST API 接口來輔助查詢跟蹤數據以實現對分布式系統的監控程序,從而及時發現系統中出現的延遲過高問題。除了面向開發的 API 接口之外,它還提供了方便的 UI 組件來幫助我們直觀地搜索跟蹤信息和分析請求鏈路明細,比如可以查詢某段時間內各用戶請求的處理時間等。

 

Zipkin 和 Config 結構類似,分為服務端 Server,客戶端 Client,客戶端就是各個微服務應用。

 

微服務中,如果服務與服務之間的依賴關系非常復雜,如果某個服務出現一些問題,很難知道原因。

 

 

 Spring Cloud提供ZipKin組件

 

SpringCloud Zipkin 與Sleuth
Zipkin 是一個開放源代碼分布式的跟蹤系統,由Twitter公司開源,它致力於收集服務的定時數據,以解決微服務架構中的延遲問題,包括數據的收集、存儲、查找和展現。
每個服務向zipkin報告計時數據,例如用戶每次請求服務的處理時間等,可方便的監測系統中存在的瓶頸。
zipkin會根據調用關系通過Zipkin UI生成依賴關系圖。

Spring Cloud Sleuth為服務之間調用提供鏈路追蹤。通過Sleuth可以很清楚的了解到一個服務請求經過了哪些服務,每個服務處理花費了多長。從而讓我們可以很方便的理清各微服務間的調用關系。此外Sleuth可以幫助我們:
耗時分析: 通過Sleuth可以很方便的了解到每個采樣請求的耗時,從而分析出哪些服務調用比較耗時;
可視化錯誤: 對於程序未捕捉的異常,可以通過集成Zipkin服務界面上看到;
鏈路優化: 對於調用比較頻繁的服務,可以針對這些服務實施一些優化措施。
Spring Cloud Sleuth可以結合Zipkin,將信息發送到Zipkin,利用Zipkin的存儲來存儲信息,利用Zipkin Ui來展示數據。

  

 

搭建Zipkin服務追蹤系統

      

Spring Boot 2.0 版本之后,官方已不推薦自己搭建定制了,而是直接提供了編譯好的 jar 包。詳情可以查看官網:https://zipkin.io/pages/quickstart.html
注意:zipkin官網已經提供定制了,使用官方jar運行即可。

啟動方式:
默認端口號啟動zipkin服務
java –jar zipkin.jar 默認端口號; 9411
訪問地址:http://192.168.18.220:9411

指定端口號啟動8080啟動zipkin服務
java -jar zipkin.jar --server.port=8080
訪問地址:http://192.168.18.220:8080

指定訪問rabbitmq 啟動
java -jar zipkin.jar --zipkin.collector.rabbitmq.addresses=127.0.0.1

 

訪問:http://192.168.8.159:9411/zipkin/

 

 默認的值是在內存中  需要設置持久化到內存中哦

 

 案例展示  order  ---> member ---> msg

在Order服務、Member、Msg服務里面引入:

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

pom:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
    </parent>
    <!-- 管理依賴 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Finchley.M7</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <!-- SpringBoot整合Web組件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- SpringBoot整合eureka客戶端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

    </dependencies>
    <!-- 注意: 這里必須要添加, 否者各種依賴有問題 -->
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/libs-milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

yml配置      收集方式有抽樣收集 有全部收集     收集到平台的ip+端口號

 

 

###會員項目的端口號
server:
  port: 8000
###服務別名----服務注冊到注冊中心名稱 
spring:
  application:
    name: app-toov5-member
  zipkin: 
    base-url: http://127.0.0.1:9411/
  ###全部采集  
  sleuth:
    sampler:
      probability: 1.0

eureka:
  client:
    service-url:
    ##### 當前會員服務注冊到eureka服務地址
      defaultZone: http://localhost:8100/eureka
    ### 需要將我的服務注冊到eureka上
    register-with-eureka: true
    ####需要檢索服務
    fetch-registry: true

 

 

底層原理:

   

服務跟蹤原理
為了實現請求跟蹤,當請求發送到分布式系統的入口端點時, 只需要服務跟蹤框架為該請求創建一個唯的跟蹤標識, 同時在分布式系統內部流轉的時候,框架始終保持傳遞該唯一標識, 直到返回給請求方為止,這個唯一標識就是前 文中提到的Trace ID。通過Trace ID的記錄,我們就能將所有請求過程的日志關聯起來。
為了統計各處理單元的時間延遲,當請求到達各個服務組件時,或是處理邏輯到達某個狀態時,也通過一個唯一 標識來標記它的開始、 具體過程以及結束,該標識就是前文中提到的Span ID。對於每個Span來說,它必須有開始和結束兩個節點,通過記錄開始Span和結束Span的時間戳,就能統計出該Span的時間延遲,除了時間戳記錄之外,它還可以包含一些其他元數據, 比如事件名稱、請求信息等
SpanId記錄每一次請求, TraceID記錄整個調用鏈全局ID

 

    TraceId 和 SpanId 在微服務中傳遞追蹤

    TraceId記錄每一次請求,耗時時間、接口調用關系

    TraceId和SpanId在微服務中傳遞追蹤

  

    在微服務中,使用請求頭傳遞TraceId和SpanId,一個TraceId由多個SpanId組合起來。獲取到整個微服務調用依賴關系

    

下一級的parentId就是上一級的spanId 形成一個鏈

每次請求生成一個新的spanId

   

在微服務中,使用請求頭傳遞TraceId和SpanId,一個TraceId由多個SpanId組合起來,獲取到整個微服務調用依賴關系。 

 

 

 

案例如下:

 Eureka略

 

 Order:

   controller:

   

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class OrderControler {

    // RestTemplate 是有SpringBoot Web組件提供 默認整合ribbon負載均衡器
    // rest方式底層是采用httpclient技術
    @Autowired
    private RestTemplate restTemplate;

    /**
     * 在SpringCloud 中有兩種方式調用 rest、fegin(SpringCloud)
     * 
     * @return
     */

    // 訂單服務調用會員服務
    @RequestMapping("/getOrder")
    public String getOrder() {
        // 有兩種方式,一種是采用服務別名方式調用,另一種是直接調用 使用別名去注冊中心上獲取對應的服務調用地址
        String url = "http://app-itmayiedu-member/getMember";
        String result = restTemplate.getForObject(url, String.class);
        System.out.println("訂單服務調用會員服務result:" + result);
        return result;
    }

    @RequestMapping("/orderToMemberMsg")
    public String orderToMemberMsg(HttpServletRequest request) {
        System.out.println(
                "TraceId:" + request.getHeader("X-B3-TraceId") + ",spanid:" + request.getHeader("X-B3-SpanId"));
        // 有兩種方式,一種是采用服務別名方式調用,另一種是直接調用 使用別名去注冊中心上獲取對應的服務調用地址
        String url = "http://app-itmayiedu-member/memberAndMsg";
        String result = restTemplate.getForObject(url, String.class);
        System.out.println("訂單服務調用會員服務result:" + result);
        return result;
    }

}

yml:

  

###訂單服務的端口號
server:
  port: 8001
###服務別名----服務注冊到注冊中心名稱 
spring:
  application:
    name: app-itmayiedu-order
  zipkin: 
    base-url: http://127.0.0.1:9411/
  ###全部采集  
  sleuth:
    sampler:
      probability: 1.0
eureka:
  client:
    service-url:
    ##### 當前會員服務注冊到eureka服務地址
      defaultZone: http://localhost:8100/eureka
    ### 需要將我的服務注冊到eureka上
    register-with-eureka: true
    ####需要檢索服務
    fetch-registry: true

 

啟動類:

  

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
public class AppOrder {
    public static void main(String[] args) {
        SpringApplication.run(AppOrder.class, args);

        // 如果使用rest方式以別名方式進行調用依賴ribbon負載均衡器 @LoadBalanced
        // @LoadBalanced就能讓這個RestTemplate在請求時擁有客戶端負載均衡的能力
    }

    // 解決RestTemplate 找不到原因 應該把restTemplate注冊SpringBoot容器中 @bean
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

 

Member:

  

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;


@RestController
public class MemberApiController {
    @Value("${server.port}")
    private String serverPort;
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/getMember")
    public String getMember(HttpServletRequest request) {

        return "this is member,我是會員服務,springcloud2.0版本!端口號:" + serverPort
                + request.getHeader("X-B3-TraceId") + ",spanid:" + request.getHeader("X-B3-SpanId");

    }

    @RequestMapping("/memberAndMsg")
    public String sndMsg() {
        String url = "http://app-itmayiedu-msg/sndMsg";
        String result = restTemplate.getForObject(url, String.class);
        System.out.println("會員服務調用消息服務result:" + result);
        return result;
    }

}

yml:

 

###會員項目的端口號
server:
  port: 8000
###服務別名----服務注冊到注冊中心名稱 
spring:
  application:
    name: app-toov5-member
  zipkin: 
    base-url: http://127.0.0.1:9411/
  ###全部采集  
  sleuth:
    sampler:
      probability: 1.0

eureka:
  client:
    service-url:
    ##### 當前會員服務注冊到eureka服務地址
      defaultZone: http://localhost:8100/eureka
    ### 需要將我的服務注冊到eureka上
    register-with-eureka: true
    ####需要檢索服務
    fetch-registry: true

 

啟動類:

 

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableEurekaClient
public class AppMember {

    // @EnableEurekaClient 將當前服務注冊到eureka上
    public static void main(String[] args) {
        SpringApplication.run(AppMember.class, args);
    }

    // 解決RestTemplate 找不到原因 應該把restTemplate注冊SpringBoot容器中 @bean
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

}

 

Msg

 

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class MsgController {

    @RequestMapping("/sndMsg")
    public String sndMsg() {
        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            // TODO: handle exception
        }
        return "我是消息服務平台";
    }

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

}

啟動類:

 

###訂單服務的端口號
server:
  port: 8003
###服務別名----服務注冊到注冊中心名稱 
spring:
  application:
    name: app-toov5-msg
  zipkin: 
    base-url: http://127.0.0.1:9411/
  ###全部采集  
  sleuth:
    sampler:
      probability: 1.0
eureka:
  client:
    service-url:
    ##### 當前會員服務注冊到eureka服務地址
      defaultZone: http://localhost:8100/eureka
    ### 需要將我的服務注冊到eureka上
    register-with-eureka: true
    ####需要檢索服務
    fetch-registry: true

 

 

 

 

 

 Eureka:

 

 訪問: http://127.0.0.1:8001/orderToMemberMsg

 

點擊上面的實際三5.024s

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM