天天看點

zipkin實作SpringCloud鍊路追蹤

一、概述

在微服務架構中,涉及服務與服務間的依賴關系非常複雜,SpringCloud 官方推薦 sleuth + zipkin 方式實作鍊路追蹤,zipkin web 管理頁面中,可以每個請求的依賴關系、請求時間、傳回時間、服務與服務間的依賴圖等。

在本文中主要介紹兩種服務依賴情況

  • 微服務調用微服務
  • 微服務通過RabbitMQ發送消息

環境依賴:

名稱 備注
JDK 1.8
RabbitMQ 3.5.6 在示範中需要啟用RabbitMQ,詳細介紹 https://blog.csdn.net/qq_36918149/article/details/100006373
SpringCloud Greenwich.SR1
Consul 1.5.2 注冊中心,Consul安裝及介紹 https://mp.csdn.net/mdeditor/95372805#
zipkin 最新版 zipkin安裝配置 https://mp.csdn.net/mdeditor/100068323#

示範資料流轉圖:

zipkin實作SpringCloud鍊路追蹤

customer-service 請求下單

waiter-service 生産訂單,MQ通知barista-service制作咖啡,咖啡制作完成後MQ通知customer-service取coffee

barista-service 制作coffee,制作完成後MQ通知waiter-service

二 、代碼

2.1 zipkin 啟動并連上RabbitMQ

java -jar zipkin.jar --zipkin.collector.rabbitmq.addresses=100.61.11.211:5672 

           

2.2 customer-service (顧客服務)

1 ) 主要pom依賴

<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
 </properties>
 <dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
    <dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-discovery</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>
		<dependency>
			<groupId>io.github.openfeign</groupId>
			<artifactId>feign-httpclient</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-zipkin</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.7</version>
		</dependency>
	</dependencies>
           

2 ) 配置檔案

application.properties

spring.application.name=customer-service
server.port=8090

customer.name=spring-${server.port}

#zipkin 位址
spring.zipkin.base-url=http://127.0.0.1:9411/
#zipkin 日志采樣比例
spring.sleuth.sampler.probability=1.0
#zipkin 推送方式
spring.zipkin.sender.type=web
# spring總是輸出日志
spring.output.ansi.enabled=ALWAYS

#rabbitmq配置
spring.rabbitmq.host=100.61.11.211
spring.rabbitmq.port=5672
spring.rabbitmq.username=1234567
spring.rabbitmq.password=1234567

# 名稱為:notifyOrders 的Exchange綁定消費端路由key為spring-8090
spring.cloud.stream.rabbit.bindings.notifyOrders.consumer.binding-routing-key=${customer.name}
           

3 ) CoffeeOrderService (rpc調用waiter-service接口)

@FeignClient(name = "waiter-service", contextId = "coffeeOrder")
public interface CoffeeOrderService {

    @PostMapping("/order/{id}")
    boolean createOrder(@PathVariable("id") Long id);
  }
           

4 ) CustomerController(客戶訂單觸發接口)

@RestController
@RequestMapping("/customer")
@Slf4j
public class CustomerController {

    @Autowired
    private CoffeeOrderService coffeeOrderService;

    @PostMapping(value = "/create/order/{id}")
    public  Boolean createOrder(@PathVariable("id") Long  id){
        log.info("customer  create order no: {}",id);
      return   coffeeOrderService.createOrder(id);
    }
           

5 ) Waiter (服務啟動時消息綁定接口)

public interface Waiter {
    String NOTIFY_ORDERS = "notifyOrders";

    @Input(NOTIFY_ORDERS)
    SubscribableChannel notification();
}

           

6 ) NotificationListener(接受coffee訂單用餐通知)

@Component
@Slf4j
public class NotificationListener {

    @StreamListener(Waiter.NOTIFY_ORDERS)
    public void takeOrder(@Payload Long id) {
        log.info("Order No: {} is Fish, I'll take it.", id);
    }
}
           

7 ) CustomerServiceApplication 啟動類

@SpringBootApplication
@Slf4j
@EnableDiscoveryClient
@EnableFeignClients
@EnableAspectJAutoProxy
@EnableBinding(Waiter.class)
public class CustomerServiceApplication {

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

    @Bean
    public CloseableHttpClient httpClient() {
        return HttpClients.custom()
                .setConnectionTimeToLive(30, TimeUnit.SECONDS)
                .evictIdleConnections(30, TimeUnit.SECONDS)
                .setMaxConnTotal(200)
                .setMaxConnPerRoute(20)
                .disableAutomaticRetries()
                .setKeepAliveStrategy(new CustomConnectionKeepAliveStrategy())
                .build();
    }
}
           

備注:@EnableBinding(Waiter.class) 綁定了MQ接口

2.3 waiter-service(服務員服務)

1 ) 主要pom依賴

<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
	</properties>
   <dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
</dependencyManagement>

     <dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-discovery</artifactId>
		</dependency>
    	<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-consul-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
		</dependency>

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

2 ) 配置檔案

application.properties

spring.application.name=customer-service
server.port=8080
#zipkin 位址
spring.zipkin.base-url=http://127.0.0.1:9411/
#zipkin 日志采樣比例
spring.sleuth.sampler.probability=1.0
#zipkin 推送方式
spring.zipkin.sender.type=web
# spring總是輸出日志
spring.output.ansi.enabled=ALWAYS

#rabbitmq配置
spring.rabbitmq.host=100.61.11.211
spring.rabbitmq.port=5672
spring.rabbitmq.username=1234567
spring.rabbitmq.password=1234567

#Exchange 交換機: finishedOrders  綁定到Queue 隊列: finishedOrders.waiter-service
spring.cloud.stream.bindings.finishedOrders.group=waiter-service
#notifyOrders 生産者路由根據 headers.customer
spring.cloud.stream.rabbit.bindings.notifyOrders.producer.routing-key-expression=headers.customer

           

3 ) Barista(服務啟動時消息綁定接口)

public interface Barista {
    String NEW_ORDERS = "newOrders";
    String FINISHED_ORDERS = "finishedOrders";

    @Input
    SubscribableChannel finishedOrders();

    @Output
    MessageChannel newOrders();
}
           

4 ) Customer(服務啟動時消息綁定接口)

public interface Customer {
    String NOTIFY_ORDERS = "notifyOrders";

    @Output(NOTIFY_ORDERS)
    MessageChannel notification();
}

           

5 ) CoffeeOrderController(提供rpc下單接口,及給barista發下單MQ)

@RestController
@RequestMapping("/order")
@Slf4j
public class CoffeeOrderController {
    @Autowired
    private Barista barista;

    @PostMapping("/{id}")
    public boolean createOrder(@PathVariable( "id") Long id){
        // 通知咖啡師有新的coffee有新的訂單
        barista.newOrders().send(MessageBuilder.withPayload(id).build());
        return  true ;
    }
}
           

6 ) OrderListener(監聽咖啡完成MQ及給customer-service 發取餐MQ)

@Component
@Slf4j
public class OrderListener {
    @Autowired
    private Customer customer;
    @Autowired
    private CoffeeOrderService orderService;

    @StreamListener(Barista.FINISHED_ORDERS)
    public void listenFinishedOrders(Long id) {
        log.info("We've finished an order no [{}].", id);
        String customerName = "張三";
        Message<Long> message = MessageBuilder.withPayload(id)
                .setHeader("customer", customerName)
                .build();
        log.info("Notify the customer: {}", customerName);
        customer.notification().send(message);
    }
}
           

7 ) WaiterServiceApplication啟動類(綁定啟動了對應MQ接口)

@SpringBootApplication
@EnableDiscoveryClient
@EnableBinding({ Barista.class, Customer.class })
public class WaiterServiceApplication implements WebMvcConfigurer {

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

備注:@EnableBinding({ Barista.class, Customer.class }) 這裡是在綁定相應的MQ

2.4 barista-service(制作coffee服務)

1 ) 主要pom依賴

<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
	</properties>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-zipkin</artifactId>
		</dependency>
   </dependencies>
           

2 ) 配置檔案

application.properties

#服務名稱
spring.application.name=barista-service
#服務端口,0表示随機端口
server.port=0
#總是推送鍊路到zipkin中
spring.output.ansi.enabled=ALWAYS
#zipkin中日志抽樣比例
spring.sleuth.sampler.probability=1.0
#zipkin中采集日志的模式
spring.zipkin.sender.type=rabbit

#actuator 相關配置
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

spring.rabbitmq.host=100.61.11.211
spring.rabbitmq.port=5672
spring.rabbitmq.username=1234567
spring.rabbitmq.password=1234567
#生成名稱為newOrders的exchange,并綁定到 名稱為 newOrders.barista-service 的Queue 上
spring.cloud.stream.bindings.newOrders.group=barista-service
           

3 ) Waiter (服務啟動時消息綁定接口)

public interface Waiter {
    String NEW_ORDERS = "newOrders";
    String FINISHED_ORDERS = "finishedOrders";

    @Input(NEW_ORDERS)
    SubscribableChannel newOrders();

    @Output(FINISHED_ORDERS)
    MessageChannel finishedOrders();
}
           

4 ) OrderListener (消息監聽及發送類)

@Component
@Slf4j
public class OrderListener {

    @Autowired
    @Qualifier(Waiter.FINISHED_ORDERS)
    private MessageChannel finishedOrdersMessageChannel;

    @StreamListener(Waiter.NEW_ORDERS)
    public void processNewOrder(Long id) {
        log.info("Barista start make  coffer order no {} .", id);
        try {
            Thread.sleep(200L);
        } catch (Exception e) {
        }
        finishedOrdersMessageChannel.send(MessageBuilder.withPayload(id).build());
        log.info("Barista notify Waiter finish coffee order no {} .", id);
    }

}
           

5 ) BaristaServiceApplication(啟動類)

@EnableJpaRepositories
@SpringBootApplication
@EnableBinding(Waiter.class)
public class BaristaServiceApplication {

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

}

           

注意:@EnableBinding(Waiter.class) 是綁定消息接口。

三、結果示範

3.1 客戶下單

url :http://localhost:8090/customer/create/order/230

zipkin實作SpringCloud鍊路追蹤

3.2 服務調用鍊路

zipkin實作SpringCloud鍊路追蹤

3.3 服務依賴關系圖

zipkin實作SpringCloud鍊路追蹤

3.4 rabbitMQ

zipkin實作SpringCloud鍊路追蹤
zipkin實作SpringCloud鍊路追蹤
zipkin實作SpringCloud鍊路追蹤

四、總結

1)在項目中,可以通過http 向zipkin推送鍊路資訊, 但是如果在生成環境資料量太大勢必會有性能問題;

2)zipkin 關于海量日志資料如何儲存, 還需在後期研究一下。