1. 简介
在使用Spring Cloud Sleuth的时候,日志在控制台展示,这样的日志首先不容易阅读观察,另外日志分散在各个微服务服务器上,所以我们一般使用zipkin统一聚合轨迹日志并进行存储展示。把 Sleuth 的数据信息发送给 Zipkin进行聚合,利用 Zipkin 存储并展示数据。
2. 结合 Zipkin展示追踪数据
Zipkin 包括Zipkin Server和 Zipkin Client两部分,Zipkin Server是一个单独的服务,Zipkin Client就是具体的微服务
- Zipkin Server 构建
- pom.xml
<dependencies>
<!--zipkin-server的依赖坐标-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.12.3</version>
<exclusions>
<!--排除掉log4j2的传递依赖,避免和springboot依赖的日志组件冲突-->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--zipkin-server ui界面依赖坐标-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>2.12.3</version>
</dependency>
</dependencies>
- 启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import zipkin2.server.internal.EnableZipkinServer;
import javax.sql.DataSource;
@SpringBootApplication
@EnableZipkinServer // 开启Zipkin 服务器功能
public class ZipkinServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class,args);
}
}
- 配置文件 application.yml
server:
port: 9411
management:
metrics:
web:
server:
auto-time-requests: false # 关闭自动检测
- Zipkin Client 构建(在具体微服务中修改)
- pom中添加 zipkin 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
- application.yml 中添加对zipkin server的引用
zipkin:
base-url: http://127.0.0.1:9411 # zipkin server的请求地址
sender:
# web 客户端将踪迹日志数据通过网络请求的方式传送到服务端,另外还有配置
# kafka/rabbit 客户端将踪迹日志数据传递到mq进行中转
type: web
sleuth:
sampler:
# 采样率 1 代表100%全部采集 ,默认0.1 代表10% 的请求踪迹数据会被采集
# 生产环境下,请求量非常大,没有必要所有请求的踪迹数据都采集分析,对于网络包括server端压力都是比较大的,可以配置采样率采集一定比例的请求的踪迹数据进行分析即可
probability: 1
- 重新启动,发起请求,查看日志
2020-09-08 00:05:40.218 DEBUG [lagou-service-resume,,,] 23782 --- [ender@4f402027}] o.s.c.s.z.s.ZipkinRestTemplateWrapper : HTTP POST http://127.0.0.1:9411/api/v2/spans
2020-09-08 00:05:40.220 DEBUG [lagou-service-resume,,,] 23782 --- [ender@4f402027}] o.s.c.s.z.s.ZipkinRestTemplateWrapper : Accept=[text/plain, application/json, application/*+json, */*]
2020-09-08 00:05:40.221 DEBUG [lagou-service-resume,,,] 23782 --- [ender@4f402027}] o.s.c.s.z.s.ZipkinRestTemplateWrapper : Writing [[B@68440f20] as "application/json"
2020-09-08 00:05:40.681 DEBUG [lagou-service-resume,,,] 23782 --- [ender@4f402027}] o.s.c.s.z.s.ZipkinRestTemplateWrapper : Response 202 ACCEPTED
如上所示,可以看到日志通过http的方式发送给 Zipkin进行聚合展示
- 打开zipkin管理控制台,如下
如上所示,Zipkin server ⻚面可以方便我们查看服务调用依赖关系及一些性能指标和异常信息。
上面的日志信息默认是保存在内存中,如果zipkinServer重新启动,那么之前统计的日志就看不到了,所以可以将追踪数据Zipkin持久化到mysql或者其他存储引擎中,此处示例存储到mysql
3. 追踪数据Zipkin持久化到mysql,在zipkin-server端配置
- mysql中创建名称为zipkin的数据库,并执行如下sql语句(官方提供) :
详细可参考:https://github.com/openzipkin/zipkin/blob/master/zipkin-storage/mysql-v1/src/main/resources/mysql.sql
其他存储方式可以参考:https://github.com/openzipkin/zipkin/tree/master/zipkin-storage
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;
- pom文件引入相关依赖
<!--zipkin针对mysql持久化的依赖-->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-mysql</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--操作数据库需要事务控制-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
- 修改配置文件,添加如下内容
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/zipkin?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
username: root
password: 123456
druid:
initialSize: 10
minIdle: 10
maxActive: 30
maxWait: 50000
# 指定zipkin持久化介质为mysql
zipkin:
storage:
type: mysql
- 启动类中注入事务管理器
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import zipkin2.server.internal.EnableZipkinServer;
import javax.sql.DataSource;
@SpringBootApplication
@EnableZipkinServer // 开启Zipkin 服务器功能
public class ZipkinServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinServerApplication.class,args);
}
// 注入事务控制器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
如上配置完成后,重启服务,后面记录的日志都会持久化到mysql数据库中。