Zipkin介绍与在SpringCloud alibaba中的使用
Zipkin
在微服务(分布式系统)等多个服务组成的系统中,一个请求是跨多个服务处理的,这样就很难理解整个请求处理流程(可追溯性)。当故障和请求延迟因可追溯性降低而加剧时,这也使得更难找到哪个服务有问题。通过可视化服务间依赖和调用的系统称为分布式跟踪系统,它的作用就是帮助去解决此类问题。
介绍
官网地址:https://zipkin.io/
Zipkin是一个分布式跟踪系统,它能聚集来自各个异构系统的实时监控数据。类似的分布式跟踪系统还有其他比较成熟的实现,例如:Naver的Pinpoint、Apache的HTrace、阿里的鹰眼Tracing、京东的Hydra、新浪的Watchman,美团点评的CAT,skywalking等。
构成
Reporter
Reporter 负责将日志从您的应用程序转发到 Zipkin 服务器,有三种主要的传输方式:HTTP、Kafka和Scribe。
Collector
跟踪数据到达Zipkin收集器守护进程后,Zipkin收集器将对其进行验证、存储和索引,以便进行查找。
Storage
负责存储的持久化,最初是为了在Cassandra上存储数据而构建的,因为Cassandra是灵活且可扩展的,并且在Twitter中被大量使用。=除了Cassandra,Zipkin原生支持ElasticSearch和MySQL。
API
API 提供以各种方式从存储中提取数据的功能。
UI
它是一个 UI,可将存储在浏览器中的日志数据可视化。
部署与使用
docker
docker run -d -p 9411:9411 openzipkin/zipkin
Java(>1.8)
curl -sSL https://zipkin.io/quickstart.sh | bash -s
java -jar zipkin.jar
源码
# get the latest source
git clone https://github.com/openzipkin/zipkin
cd zipkin
# Build the server and also make its dependencies
./mvnw -DskipTests --also-make -pl zipkin-server clean install
# Run the server
java -jar ./zipkin-server/target/zipkin-server-*exec.jar
启动后,访问 http://127.0.0.1:9411 可以看到效果:
项目演示
利用mysql启动zipkin
使用java -jar运行,zipkin默认会把信息存储到内存中,这样重启后就会丢失,这里我们利用mysql作为存储介质。
DDL如下:
CREATE TABLE IF NOT EXISTS zipkin_spans (
`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
`trace_id` BIGINT NOT NULL,
`id` BIGINT NOT NULL,
`name` VARCHAR(255) NOT NULL,
`remote_service_name` VARCHAR(255),
`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',
PRIMARY KEY (`trace_id_high`, `trace_id`, `id`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds';
ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames';
ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames';
ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range';
CREATE TABLE IF NOT EXISTS zipkin_annotations (
`trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit',
`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 CHARACTER SET=utf8 COLLATE utf8_general_ci;
ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `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 and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values';
ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job';
CREATE TABLE IF NOT EXISTS zipkin_dependencies (
`day` DATE NOT NULL,
`parent` VARCHAR(255) NOT NULL,
`child` VARCHAR(255) NOT NULL,
`call_count` BIGINT,
`error_count` BIGINT,
PRIMARY KEY (`day`, `parent`, `child`)
) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci;
运行jar包命令:
java -jar zipkin-server-2.24.0-exec.jar --STORAGE_TYPE=mysql --MYSQL_HOST=127.0.0.1 --MYSQL_TCP_PORT=3306 --MYSQL_DB=zipkin --MYSQL_USER=root --MYSQL_PASS=123456
这里博主的jar包版本为 zipkin-server-2.24.0-exec
项目依赖调用演示(springcloud alibaba)
新建项目server1和server2,并利用Feign调用从server1向server2发起一个http调用
观察zipkin
准备工作
- 到https://github.com/alibaba/nacos/releases下下载nacos 项目
到nacos项目下 nacos-server-1.1.4\nacos\bin 启动cmd脚本
双击startup.cmd
启动成功后访问 http://localhost:8848/nacos/ - 本地启动zipkin
启动后访问 http://127.0.0.1:9411/
新建springboot项目server1
完整pom文件为:
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>server1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>server1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Nacos 客户端 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<!--服务间通信组件OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!--添加Sleuth依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<!--Zipkin 客户端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
</project>
主类:
@SpringBootApplication
@EnableDiscoveryClient//开启服务注册发现功能
@EnableFeignClients//启动Feign功能
public class Server1Application {
public static void main(String[] args) {
SpringApplication.run(Server1Application.class, args);
}
}
controller
@RestController
public class SampleController {
@Resource
private BServiceFeignClient bService;
@GetMapping("/a")
public String methodA(){
String result = bService.methodB();
result = "-> Service A" + result;
return result;
}
}
client
@FeignClient("b-service")
public interface BServiceFeignClient {
@GetMapping("/b")
public String methodB();
}
yml
server:
port: 7001
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
username: nacos
password: nacos
application:
name: a-service
sleuth:
sampler: #采样器
probability: 1.0 #采样率,采样率是采集Trace的比率,默认0.1
rate: 10000 #每秒数据采集量,最多n条/秒Trace
web: #Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC
enabled: true
zipkin: #设置zipkin服务端地址
base-url: http://127.0.0.1:9411
新建springboot项目server2
pom文件和主类和server1相同
controller
@RestController
public class SampleController {
@GetMapping("/b")
public String methodA(){
String result = "";
result = "-> Service B" + result;
return result;
}
}
yml
server:
port: 7002
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
username: nacos
password: nacos
application:
name: b-service
sleuth:
sampler: #采样器
probability: 1.0 #采样率,采样率是采集Trace的比率,默认0.1
rate: 10000 #每秒数据采集量,最多n条/秒Trace
web: #Spring Cloud Sleuth 针对 Web 组件的配置项,例如说 SpringMVC
enabled: true
zipkin: #设置zipkin服务端地址
base-url: http://127.0.0.1:9411
测试
启动两个项目,访问 http://127.0.0.1:7001/a 页面输出 Service A-> Service B
观察Zipkin
点击 show 按钮查看详情
点击依赖可以查看应用间的依赖调用关系