新版本SpringCloud sleuth整合zipkin


SpringCloud Sleuth 簡介

Spring Cloud Sleuth為Spring Cloud實現了分布式跟蹤解決方案。

Spring Cloud Sleuth借鑒了Dapper的術語。

Span:基本的工作單元。Span包括一個64位的唯一ID,一個64位trace碼,描述信息,時間戳事件,key-value 注解(tags),span處理者的ID(通常為IP)。

Trace:一組Span形成的樹形結構。

Annotation:用於及時記錄存在的事件。常用的Annotation如下:

  • cs:客戶端發送(client send) 客戶端發起一個請求,表示span開始
  • sr:服務器接收(server received) 服務器接收到客戶端的請求並開始處理,sr - cs 的時間為網絡延遲
  • ss:服務器發送(server send) 服務器處理完請求准備返回數據給客戶端。ss - sr 的時間表示服務器端處理請求花費的時間
  • cr:客戶端接收(client received) 客戶端接收到處理結果,表示span結束。 cr - cs 的時間表示客戶端接收服務端數據的時間

下圖展示了Span和Trace在系統中的聯系

Sleuth 默認采用 Http 方式將 span 傳輸給 Zipkin

在application.properties文件中指定

spring.zipkin.sender.type=web

使用 RabbitMQ 異步發送 span 信息

為什么選擇 RabbitMQ 消息中間件發送 span 信息

  • sleuth 默認采用 http 通信方式,將數據傳給 zipkin 作頁面渲染,但是 http 傳輸過程中如果由於不可抗因素導致 http 通信中斷,那么此次通信的數據將會丟失。而使用中間件的話,RabbitMQ 消息隊列可以積壓千萬級別的消息,下次重連之后可以繼續消費。
  • 隨着線程增多,並發量提升之后,RabbitMQ 異步發送數據明顯更具有優勢。
  • RabbitMQ 支持消息、隊列持久化,可以通過消息狀態落庫、重回隊列、鏡像隊列等技術手段保證其高可用。

示例

示例簡介

示例包含sleuth-search、sleuth-cart、sleuth-order三個系統,用來模擬電商系統中下單的流程,用戶可以搜索商品然后立即下單,也可以搜索多個商品后加入購物車,然后下單,調用情況即 search -> cart -> order,或 search -> order。

示例使用 RestTemplate 來完成三個系統間的 http 請求響應,請求方式也都遵循Restful風格。

版本說明

版本一定要對應好,一些低版本的SpringBoot無法兼容新版本的SpringCloud和zipkin

工具 版本
SpringBoot 2.1.6.RELEASE
SpringCloud Greenwich.SR3
zipkin 2.16.2

項目結構

demo-cloudsleuth
	|- sleuth-search
	|- sleuth-cart
	|- sleuth-order
	pom.xml

導入依賴

    <!-- 引入 springboot 和 springcloud 父工程 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/>
    </parent>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
        </dependency>
        <!-- Springboot 相關 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

配置 RestTemplate,RestTemplate是SpringBoot提供的封裝好的http工具類,可以幫助我們簡化http的使用。

package com.anqi.cart.resttmplate;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        return new RestTemplate(factory);
    }

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(5000);
        factory.setReadTimeout(5000);
        return factory;
    }
}

三個系統下的application.properties,端口分別是8081 8082 8083

#server.port=8081 server.port=8082
server.port=8083 
server.servlet.context-path=/

spring.zipkin.base-url=http://localhost:9411/
spring.zipkin.service.name=sleuth-cart

#使用默認 http 方式收集 span 需要配置此項
#spring.zipkin.sender.type=web

#sleuth 使用 rabbitmq 來向 zipkin 發送數據
spring.zipkin.sender.type=rabbit
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

#設置采樣率默認為 0.1 注意之前的版本是percentage 新版本中更換為 probability
spring.sleuth.sampler.probability=1

三個系統下的RestTemplate的配置,用來簡化 http 請求

package com.anqi.cart.resttmplate;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
        return new RestTemplate(factory);
    }

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(5000);
        factory.setReadTimeout(5000);
        return factory;
    }
}
@RequestMapping("cart")
@RestController
public class CartController {
    @Autowired
    RestTemplate restTemplate;
    @Autowired
    CartService cartService;
    private static final String orderUrl = "http://localhost:8084/order/create";

    @GetMapping("/add/{cartId}")
    public String addToCart(@PathVariable("cartId") String cartId) {
        cartService.addProductToCart(cartId, "小米8");
        ResponseEntity<String> res = restTemplate.getForEntity(orderUrl, String.class);
        return res.getBody();
    }
}
@RequestMapping("order")
@RestController
public class OrderController {
    @GetMapping("/create")
    public String creatOrder() {
        System.out.println("create order");
        return "create_order";
    }
}
@RestController
public class SearchController {
    @Autowired
    RestTemplate restTemplate;

    private static final String cartUrl = "http://localhost:8083/cart/add/1";
    private static final String orderUrl = "http://localhost:8084/order/create";

    @GetMapping("/search")
    public String search() {
        ResponseEntity<String> cartRes = restTemplate.getForEntity(cartUrl, String.class);
        ResponseEntity<String> orderRes = restTemplate.getForEntity(orderUrl, String.class);
        return "cart:" + cartRes.getBody() + "- order:" + orderRes.getBody();

    }
}

運行結果分析

默認 http 傳輸 span 信息

啟動Zipkin

java -jar zipkin-server-2.16.2-exec.jar

網頁中手動訪問

http://localhost:8082/search

我們訪問zipkin站點查詢調用情況

http://localhost:9411/zipkin/traces/94b954d843012ca9

可以從下圖中完整清晰的看到三個系統的調用關系

下圖為zipkin調用預覽,我們請求四次http://localhost:8082/search來更直觀的觀察數據。在以下界面中,較為簡潔的顯示Span的個數以及調用總時延。

我們進入一個完整的調用鏈后訪問其中的一個節點得到以下數據。

以下為一次全鏈路追蹤的詳細信息,包含7個span的所有信息,以上看到的頁面展示均有以下數據加以渲染而成。

[
  {
    "traceId": "94b954d843012ca9",
    "parentId": "bab70b1e69a5f3e3",
    "id": "96387b33a823ca8f",
    "kind": "SERVER",
    "name": "get /order/create",
    "timestamp": 1569060494069123,
    "duration": 1161,
    "localEndpoint": {
      "serviceName": "sletuth-order",
      "ipv4": "192.168.0.107"
    },
    "remoteEndpoint": {
      "ipv4": "127.0.0.1",
      "port": 49863
    },
    "tags": {
      "http.method": "GET",
      "http.path": "/order/create",
      "mvc.controller.class": "OrderController",
      "mvc.controller.method": "creatOrder"
    },
    "shared": true
  },
  {
    "traceId": "94b954d843012ca9",
    "parentId": "94b954d843012ca9",
    "id": "90f7e5cfa89e0d80",
    "kind": "SERVER",
    "name": "get /order/create",
    "timestamp": 1569060494076287,
    "duration": 1296,
    "localEndpoint": {
      "serviceName": "sletuth-order",
      "ipv4": "192.168.0.107"
    },
    "remoteEndpoint": {
      "ipv4": "127.0.0.1",
      "port": 49864
    },
    "tags": {
      "http.method": "GET",
      "http.path": "/order/create",
      "mvc.controller.class": "OrderController",
      "mvc.controller.method": "creatOrder"
    },
    "shared": true
  },
  {
    "traceId": "94b954d843012ca9",
    "parentId": "94b954d843012ca9",
    "id": "bab70b1e69a5f3e3",
    "kind": "CLIENT",
    "name": "get",
    "timestamp": 1569060494063693,
    "duration": 10374,
    "localEndpoint": {
      "serviceName": "sleuth-search",
      "ipv4": "192.168.0.107"
    },
    "tags": {
      "http.method": "GET",
      "http.path": "/cart/add/1"
    }
  },
  {
    "traceId": "94b954d843012ca9",
    "parentId": "94b954d843012ca9",
    "id": "90f7e5cfa89e0d80",
    "kind": "CLIENT",
    "name": "get",
    "timestamp": 1569060494074966,
    "duration": 2848,
    "localEndpoint": {
      "serviceName": "sleuth-search",
      "ipv4": "192.168.0.107"
    },
    "tags": {
      "http.method": "GET",
      "http.path": "/order/create"
    }
  },
  {
    "traceId": "94b954d843012ca9",
    "id": "94b954d843012ca9",
    "kind": "SERVER",
    "name": "get /search",
    "timestamp": 1569060494062631,
    "duration": 16332,
    "localEndpoint": {
      "serviceName": "sleuth-search",
      "ipv4": "192.168.0.107"
    },
    "remoteEndpoint": {
      "ipv6": "::1",
      "port": 49859
    },
    "tags": {
      "http.method": "GET",
      "http.path": "/search",
      "mvc.controller.class": "SearchController",
      "mvc.controller.method": "search"
    }
  },
  {
    "traceId": "94b954d843012ca9",
    "parentId": "bab70b1e69a5f3e3",
    "id": "96387b33a823ca8f",
    "kind": "CLIENT",
    "name": "get",
    "timestamp": 1569060494067090,
    "duration": 3197,
    "localEndpoint": {
      "serviceName": "sleuth-cart",
      "ipv4": "192.168.0.107"
    },
    "tags": {
      "http.method": "GET",
      "http.path": "/order/create"
    }
  },
  {
    "traceId": "94b954d843012ca9",
    "parentId": "94b954d843012ca9",
    "id": "bab70b1e69a5f3e3",
    "kind": "SERVER",
    "name": "get /cart/add/{cartid}",
    "timestamp": 1569060494066140,
    "duration": 8150,
    "localEndpoint": {
      "serviceName": "sleuth-cart",
      "ipv4": "192.168.0.107"
    },
    "remoteEndpoint": {
      "ipv4": "127.0.0.1",
      "port": 49862
    },
    "tags": {
      "http.method": "GET",
      "http.path": "/cart/add/1",
      "mvc.controller.class": "CartController",
      "mvc.controller.method": "addToCart"
    },
    "shared": true
  }
]

使用 RabbitMQ 情況

啟動 zipkin,注意參數

java -jar zipkin-server-2.16.2-exec.jar --RABBIT_ADDRESSES=localhost:5672 --RABBIT_USER=guest --RABBIT_PASSWORD=guest --RABBIT_VIRTUAL_HOST=/

啟動 rabbitmq

rabbitmq-server

在測試的時候發現 mq 和以上方式時延相差無幾,但是隨着線程數的增加也就是並發量的增加,mq 傳輸時延將會大大低於 http。


免責聲明!

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



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