如果将分布式系统比作高速公路网,每个前端的请求就相当于高速上行驶的车辆,而处理请求的应用就是高速上的收费站,在收费站上将车辆通行信息记录成日志,包括时间、车牌、站点、公路、价格等,如果将所有收费站上的日志整合在一起,便可以通过唯一的车牌号确定该车的完整通行记录;分布式调用系统跟踪和监控就是类比这种思想,对每一次请求进行跟踪,进而明确每个请求所经过的应用、耗时等信息。
业界非常知名的分布式链路跟踪服务:
阿里:鹰眼
大众点评:CAT
美团:OCTO
京东: Hydra
Twitter—OpenZipkin
鹰眼系统简介:
阿里中分布式调用跟踪是采用鹰眼(EagleEye)系统来实现的,鹰眼是基于日志的分布式调用跟踪系统,其理念脱胎于Google Dapper论文,其关键核心在于调用链,为每个请求生成全局唯一的ID(Traceld),通过它将不同系统的“孤立的”调用信息关联在一起,还原出更多有价值的数据。
可以在业务异常日志的错误信息中找到Traceld(比如TraceId=ac18287913742691251746923),之后在鹰眼系统中只需要输入Traceld,就可以看到调用链中具体的情况,在调用链上更加直观地定位到问题,层层排查后确定问题的所在。
springcloud 整合ZipKin
ZipKin是一个链路跟踪工具,可以用来监控微服务集群中调用链路的通畅情况
首先,ZipKin是Spring Cloud 的分布式链路跟踪解决方案:
比如存在两个子项目,并在一个项目中使用RestTemplate或者Feign等方法调用另外一个项目中的接口,这样就可以利用ZipKin进行跟踪服务!
第一步:需要一个zipkin-server服务
第二步:需要两个trace服务,然后进行调用跟踪
代码实现
1.server/serversteam / eureka/多个trace1(服务提供者)/trace2
server端代码实现
pom代码:
最好把数据存到ES上:数据量大,查询快
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
</dependency>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bfxy</groupId>
<artifactId>spring-cloud-master</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-08-zipkin-server</artifactId>
<dependencies>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<!-- <version>Dalston.SR5</version> -->
<version>Edgware.SR4</version>
<!-- <version>Finchley.SR1</version> -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>spring-cloud-08-zipkin-server</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.bfxy.springcloud.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置文件:zipkin.storage.type=mysql
zipkin标准表:官网能下载
spring.datasource.schema=classpath:/zipkin.sql
表示加载resources下的sql文件
spring.datasource.schema=classpath:/zipkin.sql
spring.datasource.initialize=true
spring.application.name=zipkin-server
server.port=9500
spring.datasource.url=jdbc:mysql://localhost:3306/zipkin?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.schema=classpath:/zipkin.sql
spring.datasource.initialize=true
spring.datasource.continue-on-error=true
zipkin.storage.type=mysql
CREATE TABLE IF NOT EXISTS zipkin_spans (
`trace_id` BIGINT NOT NULL,
`id` BIGINT NOT NULL,
`name` VARCHAR(255) NOT NULL,
`parent_id` BIGINT,
`debug` BIT(1),
`start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL',
`duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED;
ALTER TABLE zipkin_spans ADD UNIQUE KEY(`trace_id`, `id`) COMMENT 'ignore insert on duplicate';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id`, `id`) COMMENT 'for joining with zipkin_annotations';
ALTER TABLE zipkin_spans ADD INDEX(`trace_id`) COMMENT 'for getTracesByIds';
ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';
CREATE TABLE IF NOT EXISTS zipkin_annotations (
`trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id',
`span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id',
`a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1',
`a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB',
`a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation',
`a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp',
`endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null',
`endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address',
`endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null',
`endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null'
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED;
ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`) COMMENT 'for getTraces/ByIds';
ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames';
ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces';
CREATE TABLE IF NOT EXISTS zipkin_dependencies (
`day` DATE NOT NULL,
`parent` VARCHAR(255) NOT NULL,
`child` VARCHAR(255) NOT NULL,
`call_count` BIGINT
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED;
ALTER TABLE zipkin_dependencies ADD UNIQUE KEY(`day`, `parent`, `child`);
主入口:@zipkin.server.internal.EnableZipkinServer
package com.bfxy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@zipkin.server.internal.EnableZipkinServer
@SpringBootApplication //SpringBoot 核心配置
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
此时http://localhost:9500 就可以访问zipken界面
trace1(注册到注册中心中):代码
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bfxy</groupId>
<artifactId>spring-cloud-master</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-08-trace-1</artifactId>
<name>spring-cloud-08-trace-1</name>
<description>spring-cloud-08-trace-1</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 标识这个工程是一个服务,需要引入此jar -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 动态刷新的一个模块jar -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- sleuth & zipkin -->
<!--
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
-->
<!-- kafka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<!-- <version>Dalston.SR5</version> -->
<version>Edgware.SR4</version>
<!-- <version>Finchley.SR1</version> -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>spring-cloud-08-trace-1</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.bfxy.springcloud.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
主入口:
package com.bfxy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient //标识具体的一个服务,需要向注册中心注册
@SpringBootApplication //SpringBoot 核心配置
public class Application {
@Bean
@LoadBalanced //用于实现内部的服务负载均衡机制: service-id service-name
public RestTemplate restTemplate(){
HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
httpComponentsClientHttpRequestFactory.setConnectTimeout(10000);
httpComponentsClientHttpRequestFactory.setConnectionRequestTimeout(10000);
httpComponentsClientHttpRequestFactory.setReadTimeout(20000);
return new RestTemplate(httpComponentsClientHttpRequestFactory);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
配置:
## 配置zipkin地址 以及sleuth服务抓取日志的采样百分比
spring.zipkin.base-url=http://localhost:9500
spring.sleuth.sampler.percentage=1.0 ##100%请求都会被采集, 0.8表示80%请求会被采集
##bus总线代码
spring.cloud.stream.kafka.binder.zkNodes=192.168.11.111:2181,192.168.11.112:2181,192.168.11.113:2181
spring.cloud.stream.kafka.binder.brokers=192.168.11.51:9092
spring.application.name=trace-1
server.context-path=/
server.port=7001
##需要引入eureka注册中心的地址
eureka.instance.prefer-ip-address=true
eureka.instance.instance-id=${spring.application.name}:${spring.cloud.client.ipAddress}:${server.port}
##租期更新时间间隔
eureka.instance.lease-renewal-interval-in-seconds=10
##租期的到期时间间隔
eureka.instance.lease-expiration-duration-in-seconds=30
##开启健康检查(必须要引入spring-boot-starter-actuator)
eureka.client.healthcheck.enabled=true
eureka.client.service-url.defaultZone=http://eureka1:8001/eureka
## 配置zipkin地址 以及sleuth服务抓取日志的采样百分比
spring.zipkin.base-url=http://localhost:9500
spring.sleuth.sampler.percentage=1.0
spring.cloud.stream.kafka.binder.zkNodes=192.168.11.111:2181,192.168.11.112:2181,192.168.11.113:2181
spring.cloud.stream.kafka.binder.brokers=192.168.11.51:9092
测试请求:
package com.bfxy.springcloud.api;
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 Trace1Controller {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value="/trace1")
public String trace1(){
System.err.println("--------------trace1-----------");
return restTemplate.getForObject("http://trace-2/trace2", String.class);
}
}
所有对接口请求的数据都会被监听。在zipkin中生成数据;如果trace1中调用trace2,trace2中调用trace3。则zipkin会根据采集请求概率对三个服务进行采集
数据库中就会出现三条数据。
使用kafka收集数据到zipkin
pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bfxy</groupId>
<artifactId>spring-cloud-master</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-08-zipkin-server-stream</artifactId>
<dependencies>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<!-- <version>Dalston.SR5</version> -->
<version>Edgware.SR4</version>
<!-- <version>Finchley.SR1</version> -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<finalName>spring-cloud-08-zipkin-server-stream</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.bfxy.springcloud.Application</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置:
spring.cloud.stream.kafka.binder.zkNodes=192.168.11.111:2181,192.168.11.112:2181,192.168.11.113:2181
spring.cloud.stream.kafka.binder.brokers=192.168.11.51:9092
zipkin.storage.type=mysql
spring.application.name=zipkin-server
server.port=9500
spring.datasource.url=jdbc:mysql://localhost:3306/zipkin?characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useUnicode=true
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.schema=classpath:/zipkin.sql
spring.datasource.initialize=true
spring.datasource.continue-on-error=true
spring.sleuth.enabled=false
spring.sleuth.sampler.percentage=1.0
spring.cloud.stream.kafka.binder.zkNodes=192.168.11.111:2181,192.168.11.112:2181,192.168.11.113:2181
spring.cloud.stream.kafka.binder.brokers=192.168.11.51:9092
zipkin.storage.type=mysql
##elasticsearch config
#zipkin.storage.type=elasticsearch
#zipkin.storage.elasticsearch.hosts=192.168.11.34:9200,192.168.11.35:9200,192.168.11.36:9200
#zipkin.storage.elasticsearch.cluster=elasticsearch_zipkib
#zipkin.storage.elasticsearch.index=zipkin
#zipkin.storage.elasticsearch.index-shards=5
#zipkin.storage.elasticsearch.index-replicas=3
主入口:@EnableZipkinStreamServer
或者使用
@EnableKafkaStreams但是会提时缺少jar包
package com.bfxy.springcloud;
import java.util.concurrent.ThreadLocalRandom;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer;
import org.springframework.kafka.annotation.EnableKafkaStreams;
import zipkin.server.EnableZipkinServer;
@EnableZipkinStreamServer
//@EnableKafkaStreams //用这个注解会提示缺少kafka jar
@SpringBootApplication //SpringBoot 核心配置
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
三个服务提供者也需要集成kafka:
服务提供者:
<!-- kafka --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-kafka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId> </dependency>