微服务系统是一个分布式架构的系统,微服务系统按业务划分服务单元,一个微服务系统往往有很多个服务单元。由于服务单元数量很多且业务复杂,服务与服务之间的调用可能非常复杂,一旦出现了异常和错误,就会很难去定位。所以在微服务架构中,必须实现分布式链路追踪,去跟进一个请求到底有哪些服务参与,参与的顺序又是怎样的,从而使每个请求的链路清晰可见,出了问题很快能定位。
常见的链路追踪组件有Google的Dapper,Twitter的Zipkin,以及阿里的Eagleeye(鹰眼)等。
基本术语:
1 Span(跨度):基本工作单元,发送一个远程调度任务就会产生一个Span,Span是一个用64位ID唯一标识的,Trace是用另一个64位ID唯一标识的。Span还包含了其他的信息,例如摘要、时间戳事件、Span的ID以及进程ID
2 Trace(痕迹):由一系列Span组成的,呈树状结构。请求一个微服务系统的API接口,这个API接口需要调用多个微服务单元,调用每个微服务单元都会产生一个新的Span,所有由这个请求产生的Span组成了这个Trace
3 Annotation(标注):用于记录一个事件,一些核心注解用于定义一个请求的开始和结束,这些注解如下
cs-Client Sent:客户端发送一个请求,这个注解描述了Span的开始
sr-Server Received:服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳,便可得到网络传输的时间
ss-Server Sent:服务端发送响应,该注解表明请求处理的完成(当请求返回客户端),用ss的时间戳减去sr时间戳,便可得到服务器请求的时间
cr-Client Received:客户端接收响应,此时Span结束,如果cr的时间戳减去cs时间戳,便可以得到整个请求所消耗的时间
框架中如何引用配置:(第一版)
zipkin参考:https://blog.csdn.net/qq_15144655/article/details/80020199
链路跟踪里添加自定义标签:
@Autowired
Tracer tracer;
public void run() {
...
tracer.addTag("operator","forezp");
System.out.println(tracer.getCurrentSpan().traceIdString());
}
使用RabbitMQ传输链路数据:(第二版)
需要改造:
1 zipkin-server工程,在其pom文件中将zipkin-server的依赖去掉,加上spring-
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId></dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
在yml加上RabbitMQ配置
spring:
rabbitmq:
hots: localhost
port: 5672
username: guest
password: guest
启动类:@EnableZipkinServer 改成 @EnableZipkinStreamServer
@EnableDiscoveryClient
@EnableZipkinStreamServer
@SpringBootApplication
public class ZipkinApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(ZipkinApplication.class, args);
}
}
改造Zipkin Client :
pom文件中的spring-cloud-starter-zipkin 依赖改成 spring-cloud-sleuth-zipkin-stream 和 spring-cloud-starter-stream-rabbit(和server一样)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId></dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
同时在配置文件yml上加上RabbitMQ的配置,这样,就将链路上的上传数据从HTTP改成消息代组件RabbitMQ
上面的例子中zipkin Server将数据存储在内存中,一旦程序重启,之前的链路数据全部丢失。Zipkin支持将链路数据存储在MySQL、Elasticsearch和Cassandra数据库中。
在MySQL数据库中存储链路数据
Zipkin Client有两种方式将链路数据传输到Zipkin Server中,一种是Http,另一种是RabbitMQ。这两种方式在具体编码实现上有较大区别,分开讲解。
1 使用Http传输链路数据,并存储在MySQL
我们在第一版的基础上改造,只需要改造Zipin Server工程
1 Zipkin Server:zipkin-server(zipkin依赖),zipkin-storage-mysql(Zipkin的MySQL存储依赖),zipkin-autocinfigure-ui(UI界面依赖)
2 mysql-connector-java(MYSQL的连接器依赖)
3 spring-boot-starter-jdbc(JDBC的起步依赖)
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>1.23.2</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-storage-mysql</artifactId>
<version>1.23.2</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-ui</artifactId>
<version>2.11.6</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
yml文件加上数据源配置
spring:
datasource:
type : com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.0.72:3306/mysql?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
zipkin:
storage:
type: mysql
初始化数据库脚本
最后在程序启动类ZipkinServerApplication中注入MySQLStorage的Bean
@Bean
public MySQLStorage mySQLStorage(DataSource datasource){
return MySQLStorage.builder().datasource(datasource).executor(Runnable::run).build();
}
使用RabbitMQ传输链路数据,并存储在MYSQL中
在第二版中进行改造,只需要改造zipkin-server
pom文件中加上MySQL的连接器依赖mysql-connector-java,JDBC的起步依赖spring-boot-starter-jdbc
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>6.0.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
在yml文件配置数据库信息
spring:
application:
name: yycp-security-service
http :
encoding :
force : true
charset : UTF-8
enabled : true
datasource:
type : com.mysql.jdbc.Driver
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.0.72:3306/mysql?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
zipkin:
storage:
type: mysql
另外初始化脚本,和上一节一样。
在ES中存储链路数据
在高并发的情况下,使用MySQL存储链路数据显然不合理,这时可以选择使用ES存储。(自行安装ElasticSerach和Kibana)
ES默认端口9200,Kibana默认端口5601
pom文件配置
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin</artifactId>
<version>1.28.0</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId>
<version>1.28.0</version>
</dependency>
yml配置:
spring:
datasource:
type : com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.0.72:3306/mysql?useUnicode=true&characterEncoding=utf8&useSSL=false
username: root
password: root
zipkin:
storage:
type: mysql
yml文件配置
zipkin:
storage:
type: elasticsearch
storageComponent: elasticsearch
elasticsearch:
cluster: elasticsearch
max-requests: 30
index: zipkin
index-shards: 3
index-replicas: 1
hosts: localhost:9200
只需配置这些链路数据就存在ES里了
用Kibaba展示链路数据
ES可以和Kibaba结合,展示在kibaba上
kibana安装完成启动后,会默认向本地端口位9200的ES读取数据
访问kibana的主页:http://localhost:5601
1 点击 “Management” 按钮,然后点击 “AddNew”,添加index。
2 我们将ES中链路数据的index配置为 zipkin, 那么界面填写为:zipkin-*,点击 Create 按钮。
3 创建完成index后,点击 “Discover” 就可以在界面上展示链路数据了
学习springcloud参考:https://springcloud.cc/spring-cloud-dalston.html#_custom_sa_tag_in_zipkin