SpringBoot集成Zipkin实现分布式全链路监控
Zipkin 简介
Zipkin is a distributed tracing system. It helps gather timing data needed to troubleshoot latency problems in service architectures. Features include both the collection and lookup of this data.
If you have a trace ID in a log file, you can jump directly to it. Otherwise, you can query based on attributes such as service, operation name, tags and duration. Some interesting data will be summarized for you, such as the percentage of time spent in a service, and whether or not operations failed.
Application’s need to be “instrumented” to report trace data to Zipkin. This usually means configuration of a tracer or instrumentation library. The most popular ways to report data to Zipkin are via http or Kafka, though many other options exist, such as Apache ActiveMQ, gRPC and RabbitMQ. The data served to the UI is stored in-memory, or persistently with a supported backend such as Apache Cassandra or Elasticsearch.
Zipkin是一种分布式跟踪系统。 它有助于收集解决服务体系结构中的延迟问题所需的计时数据。 功能包括收集和查找此数据。
Zipkin是Twitter基于google的分布式监控系统Dapper(论文)的开发源实现,zipkin用于跟踪分布式服务之间的应用数据链路,分析处理延时,帮助我们改进系统的性能和定位故障。Dapper论文地址
如果日志文件中有跟踪ID,则可以直接跳转到该文件。 否则,你可以根据服务,操作名称,tag标签和持续时间等属性进行查询。 将为您总结一些有趣的数据,例如在服务中花费的时间占比,以及操作是否失败。
应用程序需要“检测”以向Zipkin报告跟踪数据。 这通常意味着配置跟踪器或检测库。 向Zipkin报告数据的最常用方法是通过http或Kafka,尽管存在许多其他选项,例如Apache ActiveMQ,gRPC和RabbitMQ。 提供给UI的数据存储在内存中,或者持久存储在受支持的后端(如Apache Cassandra或Elasticsearch)中。
本示例中是使用Zipkin中集成的http组件进行发送Span数据。
Springboot 集成 Zipkin
安装启动 zipkin
https://github.com/openzipkin/zipkin 中下载 zipkin.jar
java -jar zipkin.jar
版本说明
| 框架组件 | Version |
|---|---|
| springboot | 2.1.6.RELEASE |
| zipkin | 3.9.0 |
项目结构
项目采用父工程集成多模块的方式构建而成,demo-zipkin 父工程聚合了zipkin-1、zipkin-2、zipkin-3、zipkin-4、zipkin-5 五个 Module。
demo-zipkin
zipkin-1
|-SpanCollectorConfig
|-application.properties
|-ZipkinController
|-Application1
zipkin-2
|-SpanCollectorConfig
|-application.properties
|-ZipkinController2
|-Application1
zipkin-3
|-SpanCollectorConfig
|-application.properties
|-ZipkinController3
|-Application1
zipkin-4
|-SpanCollectorConfig
|-application.properties
|-ZipkinController4
|-Application1
zipkin-5
|-SpanCollectorConfig
|-application.properties
|-ZipkinController5
|-Application1
工程端口分配
每个 Module 使用不同的端口,分别启动自己的Application。
| Module名称 | 端口 | Application |
|---|---|---|
| zipkin-1 | 8081 | Application1 |
| zipkin-2 | 8082 | Application2 |
| zipkin-3 | 8083 | Application3 |
| zipkin-4 | 8084 | Application4 |
| zipkin-5 | 8085 | Application5 |
引入 Maven 依赖
<properties>
<zipkin.version>3.9.0</zipkin.version>
</properties>
<!-- Springboot 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- zipkin相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-core</artifactId>
<version>${zipkin.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-spancollector-http</artifactId>
<version>${zipkin.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-web-servlet-filter</artifactId>
<version>${zipkin.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-apache-http-interceptors</artifactId>
<version>${zipkin.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-okhttp</artifactId>
<version>${zipkin.version}</version>
</dependency>
配置文件、收集器的设置
配置 application.properties,以 zipkin-1 为例,其他工程中配置时将 zipkin.serviceName与server.port更改为 myzipkin-2 …与8081...等即可
zipkin.serviceName=myzipkin-1
zipkin.url=http://localhost:9411
zipkin.connectTimeout=6000
zipkin.readTimeout=6000
zipkin.flushInterval=1
zipkin.compressionEnabled=true
zipkin.samplerRate=1
server.port=8081
server.servlet.context-path=/
配置Span收集器
设置收集器的详细参数,包含超时时间、上传span间隔、以及配置采集率等,进而对收集器进行初始化。
package com.anqi.zipkin.bean;
import com.github.kristofa.brave.Brave;
import com.github.kristofa.brave.Brave.Builder;
import com.github.kristofa.brave.EmptySpanCollectorMetricsHandler;
import com.github.kristofa.brave.Sampler;
import com.github.kristofa.brave.SpanCollector;
import com.github.kristofa.brave.http.DefaultSpanNameProvider;
import com.github.kristofa.brave.http.HttpSpanCollector;
import com.github.kristofa.brave.http.HttpSpanCollector.Config;
import com.github.kristofa.brave.okhttp.BraveOkHttpRequestResponseInterceptor;
import com.github.kristofa.brave.servlet.BraveServletFilter;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpanCollectorConfig {
@Value("${zipkin.url}")
private String url;
@Value("${zipkin.serviceName}")
private String serviceName;
/*
连接超时时间
*/
@Value("${zipkin.connectTimeout}")
private int connecTimeout;
/*
是否启动压缩
*/
@Value("${zipkin.compressionEnabled}")
private boolean compressionEnabled;
/*
上传 span 的间隔时间
*/
@Value("${zipkin.flushInterval}")
private int flushInterval;
/*
读取超时时间
*/
@Value("${zipkin.readTimeout}")
private int readTimeout;
@Value("${zipkin.samplerRate}")
private float samplerRate;
/**
* 配置 span 收集器
* @return
*/
@Bean
public SpanCollector spanCollector() {
Config config = Config.builder()
.connectTimeout(connecTimeout)
.compressionEnabled(compressionEnabled)
.flushInterval(flushInterval)
.readTimeout(readTimeout)
.build();
return HttpSpanCollector.create(url, config, new EmptySpanCollectorMetricsHandler());
}
/**
* 配置采集率
* @param spanCollector
* @return
*/
@Bean
public Brave brave(SpanCollector spanCollector) {
Builder builder = new Builder(serviceName);
builder.spanCollector(spanCollector)
.traceSampler(Sampler.create(samplerRate))
.build();
return builder.build();
}
/**
* @Description: 设置server的(服务端收到请求和服务端完成处理,并将结果发送给客户端)过滤器
* @Param:
* @return: 过滤器
*/
@Bean
public BraveServletFilter braveServletFilter(Brave brave) {
BraveServletFilter filter = new BraveServletFilter(brave.serverRequestInterceptor(),
brave.serverResponseInterceptor(), new DefaultSpanNameProvider());
return filter;
}
/**
* @Description: 设置client的 rs和cs的拦截器
* @Param:
* @return: OkHttpClient 返回请求实例
*/
@Bean
public OkHttpClient okHttpClient(Brave brave) {
OkHttpClient httpClient = new OkHttpClient.Builder()
.addInterceptor(new BraveOkHttpRequestResponseInterceptor(
brave.clientRequestInterceptor(),
brave.clientResponseInterceptor(),
new DefaultSpanNameProvider())).build();
return httpClient;
}
}
编写 Controller 发送请求进行测试
Zipkin-1中的Controller
package com.anqi.zipkin.controller;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("zipkin")
public class ZipkinController {
public static final String url = "http://localhost:8082/zipkin/service2";
@Autowired
OkHttpClient client;
@GetMapping("/service1")
public String service() {
Request request = new Request.Builder().url(url).build();
Response response;
try {
response = client.newCall(request).execute();
return response.body().string();
} catch (Exception e) {
e.printStackTrace();
}
return "null";
}
}
Zipkin-2中的Controller
package com.anqi.zipkin.controller;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("zipkin")
public class ZipkinController2 {
public static final String url = "http://localhost:8083/zipkin/service3";
public static final String url2 = "http://localhost:8084/zipkin/service4";
@Autowired
OkHttpClient client;
@GetMapping("/service2")
public String service() throws Exception {
System.out.println("loading-----");
Request request1 = new Request.Builder().url(url).build();
Request request2 = new Request.Builder().url(url2).build();
Response response1 = client.newCall(request1).execute();
Response response2 = client.newCall(request2).execute();
return "con2 + "+ response1.body().string() + "-" + response2.body().string();
}
}
Zipkin-3中的Controller
package com.anqi.zipkin.controller;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("zipkin")
public class ZipkinController3 {
public static final String url = "http://localhost:8084/zipkin/service4";
@Autowired
OkHttpClient client;
@GetMapping("/service3")
public String service() throws Exception {
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
return "con3 + "+ response.body().string();
}
}
Zipkin-4中的Controller
package com.anqi.zipkin.controller;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("zipkin")
public class ZipkinController4 {
public static final String url = "http://localhost:8085/zipkin/service5";
@Autowired
OkHttpClient client;
@GetMapping("/service4")
public String service() throws Exception {
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
return "con4 + "+ response.body().string();
}
}
Zipkin-5中的Controller
package com.anqi.zipkin.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("zipkin")
public class ZipkinController5 {
@GetMapping("/service5")
public String service() throws Exception {
return "service5 -----";
}
}
Springboot 启动类
package com.anqi.zipkin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application1 {
public static void main(String[] args) {
SpringApplication.run(Application1.class);
}
}
运行分析
在地址栏请求urlhttp://localhost:8081/zipkin/service1,然后访问http://localhost:9411/zipkin/
查看服务调用耗时

查看服务依赖关系

核心概念
通过上图可以了解到共有7个span,分别为
{
"myzipkin-1":["Server Start", "Server Finish"],
"myzipkin-1,myzipkin-2":
["Client Start", "Server Start", "Client Finish", "Server Finish"],
"myzipkin-2,myzipkin-3":
["Client Start", "Server Start", "Client Finish", "Server Finish"],
"myzipkin-3,myzipkin-4":
["Client Start", "Server Start", "Client Finish", "Server Finish"],
"myzipkin-4,myzipkin-5":
["Client Start", "Server Start", "Client Finish", "Server Finish"],
"myzipkin-2,myzipkin-4":
["Client Start", "Server Start", "Client Finish", "Server Finish"],
"myzipkin-4,myzipkin-5":
["Client Start", "Server Start", "Client Finish", "Server Finish"],
}
在json文件中选取两个子集进行分析
- 基本数据:用于跟踪树中节点的关联和界面展示,包括traceId、spanId、parentId、name、timestamp和duration,其中parentId为null的Span将成为跟踪树的根节点来展示,当然它也是调用链的起点,为了节省一次spanId的创建开销,让顶级Span变得更明显,顶级Span中spanId将会和traceId相同。timestamp用于记录调用的起始时间,而duration表示此次调用的总耗时,所以timestamp+duration将表示成调用的结束时间,而duration在跟踪树中将表示成该Span的时间条的长度。需要注意的是,这里的name用于在跟踪树节点的时间条上展示。
- traceId:标记一次请求的跟踪,相关的Spans都有相同的traceId。
- kind :zipkin最新V2版本的API中,不再要求在annotations中上传cs,cr,sr,ss。而是通过kind标记是server-side span还是client-side span,两端记录自己的timestap来取代cs和sr,记录duration来取代cr和ss
{
"traceId": "867a9e3867736b17",
"parentId": "96f19423db38c90d",
"id": "6c9fd521b6589b7f",
"kind": "SERVER",
"name": "get",
"timestamp": 1568103422569000,
"duration": 6000,
"localEndpoint": {
"serviceName": "myzipkin-4",
"ipv4": "192.168.1.160"
},
"tags": {
"http.status_code": "200",
"http.url": "/zipkin/service4"
}
},
{
"traceId": "867a9e3867736b17",
"parentId": "867a9e3867736b17",
"id": "96f19423db38c90d",
"kind": "CLIENT",
"name": "get",
"timestamp": 1568103422447000,
"duration": 139000,
"localEndpoint": {
"serviceName": "myzipkin-1",
"ipv4": "192.168.1.160"
},
"tags": {
"http.url": "http://localhost:8082/zipkin/service2"
}
}
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=281wsnsiy2dcs
文章内容为Hai Xiang原创,转载请注明链接。
SpringBoot集成Zipkin实现分布式全链路监控的更多相关文章
- 全链路监控系统开源Pinpoint入门视频教程(最新版本1.8)
pinpoint支持的模块 源码:https://github.com/naver/pinpoint技术概述:https://skyao.gitbooks.io/learning-pinpoint/c ...
- 每天学点SpringCloud(十二):Zipkin全链路监控
Zipkin是SpringCloud官方推荐的一款分布式链路监控的组件,使用它我们可以得知每一个请求所经过的节点以及耗时等信息,并且它对代码无任何侵入,我们先来看一下Zipkin给我们提供的UI界面都 ...
- SpringBoot集成Redis 一 分布式锁 与 缓存
1.添加依赖及配置(application.yml) <!-- 引入redis依赖 --> <dependency> <groupId>org.springfram ...
- 基于docker部署skywalking实现全链路监控
一.概述 简介 skywalking是一个开放源码的,用于收集.分析,聚合,可视化来自于不同服务和本地基础服务的数据的可观察的平台,skywalking提供了一个简单的方法来让你对你的分布式系统甚至是 ...
- springboot集成elk实现分布式日志管理
1.安装elk https://www.cnblogs.com/xuaa/p/10769759.html 2.idea创建springboot项目 File -> New -> Proje ...
- APM全链路监控--日志收集篇
一.监控的意义: 随着互联网普及的广度和深度,对于项目的监控显得格外重要:无论是web服务器进程.内存.cpu等资源监控,还是爬虫程序请求频率,状态码以及储存结果的监控,都需要一个及时的反馈机制. 二 ...
- 分布式链路监控与追踪系统Zipkin
1.分布式链路监控与追踪产生背景2.SpringCloud Sleuth + Zipkin3.分布式服务追踪实现原理4.搭建Zipkin服务追踪系统5.搭建Zipkin集成RabbitMQ异步传输6. ...
- Spring Cloud Sleuth + Zipkin 链路监控
原文:https://blog.csdn.net/hubo_88/article/details/80878632 在微服务系统中,随着业务的发展,系统会变得越来越大,那么各个服务之间的调用关系也就变 ...
- 高德全链路压测平台TestPG的架构与实践
导读 2018年十一当天,高德DAU突破一个亿,不断增长的日活带来喜悦的同时,也给支撑高德业务的技术人带来了挑战.如何保障系统的稳定性,如何保证系统能持续的为用户提供可靠的服务?是所有高德技术人面临的 ...
随机推荐
- SpringMVC项目案例之---数据的获取与显示
数据的获取与显示 (一)功能 1.对用户输入的数据进行获取 2.将获取的数据显示到页面 3.使用了SpringMVC技术的注解方式 4.使用了过滤器,处理中文乱码问题 5.在web.xml中设置了访问 ...
- oracle RAC LOG_ARCHIVE_DEST_1 与 LOG_ARCHIVE_DEST 冲突解决
在做 oracle RAC 归档日志配置时,出现了一个错误,开始看资料的时候, 注意到了 LOG_ARCHIVE_DEST_n 与 LOG_ARCHIVE_DEST 不能同时使用, 但在配置的时候并没 ...
- 佳木斯集训Day6
T1还是个找规律啊,记下b的个数,然后直接*2%10000000009就好了 #include <bits/stdc++.h> #define mo 1000000007 using na ...
- 总结Idea环境,吐血踩过的坑
1)首先是JDK环境安装,这一步千万要出错,我就是配错了CLASSPATH导致了很诡异的问题.可能结果:就是RUN到tomcat不报错,但是有404错误. 2)然后是IDEA安装,这里要十分注意如果你 ...
- [NUnit] discover test finished: 0 found issue
%Temp%\VisualStudioTestExplorerExtensions & restart visual studio
- Liunx查看后1000行的命令以及查看中间部分
linux 如何显示一个文件的某几行(中间几行) [一]从第3000行开始,显示1000行.即显示3000~3999行 cat filename | tail -n +3000 | head -n 1 ...
- 深入理解struts的运行机制
扫码关注公众号,不定期更新干活 在此申明本博文并非原创,原文:http://blog.csdn.net/lenotang/article/details/3336623,本文章是在此文章基础上进行优化 ...
- python2.7官方文档阅读笔记
官方地址:https://docs.python.org/2.7/tutorial/index.html 本笔记只记录本人不熟悉的知识点 The Python Tutorial Index 1 Whe ...
- cs231n---卷积网络可视化,deepdream和风格迁移
本课介绍了近年来人们对理解卷积网络这个“黑盒子”所做的一些可视化工作,以及deepdream和风格迁移. 1 卷积网络可视化 1.1 可视化第一层的滤波器 我们把卷积网络的第一层滤波器权重进行可视化( ...
- 7.17 正则表达式 re模块
在介绍正则表达式和re模块之前,先简要介绍一下 正则表达式与re模块的关系 1.正则表达式是一门独立的技术,任何语言均可使用 2.python中要想使用正则表达式需要通过re模块 正则表达式 元字符 ...