一, Zipkin 介绍
Zipkin 是什么?
Zipkin 的官方介绍: https://zipkin.apache.org/
Zipkin 是一款开源的分布式实时数据追踪系统 (Distributed Tracking System), 基于 Google Dapper 的论文设计而来, 由 Twitter 公司开发贡献. 其主要功能是聚集来自各个异构系统的实时监控数据. 分布式跟踪系统还有其他比较成熟的实现, 例如: Naver 的 Pinpoint,Apache 的 HTrace, 阿里的鹰眼 Tracing, 京东的 Hydra, 新浪的 Watchman, 美团点评的 CAT,skywalking 等.
为什么用 Zipkin?
随着业务越来越复杂, 系统也随之进行各种拆分, 特别是随着微服务架构和容器技术的兴起, 看似简单的一个应用, 后台可能有几十个甚至几百个服务在支撑; 一个前端的请求可能需要多次的服务调用最后才能完成; 当请求变慢或者不可用时, 我们无法得知是哪个后台服务引起的, 这时就需要解决如何快速定位服务故障点, Zipkin 分布式跟踪系统就能很好的解决这样的问题.
Zipkin 的一些基本概念?
Brave
Brave 是用来装备 Java 程序的类库, 提供了面向 Standard Servlet,Spring MVC,Http Client,JAX RS,Jersey,Resteasy 和 MySQL 等接口的装备能力, 可以通过编写简单的配置和代码, 让基于这些框架构建的应用可以向 Zipkin 报告数据. 同时 Brave 也提供了非常简单且标准化的接口, 在以上封装无法满足要求的时候可以方便扩展与定制.
如下图是 Brave 的结构图. Brave 利用 reporter 向 Zipkin 的 Collector 发送 trace 信息.
Brave 主要是利用拦截器在请求前和请求后分别埋点. 例如 Spingmvc 监控使用 Interceptors,MySQL 监控使用 statementInterceptors. 同理 Dubbo 的监控是利用 com.alibaba.dubbo.rpc.Filter 来过滤生产者和消费者的请求.
traceId
一次请求全局只有一个 traceId. 用来在海量的请求中找到同一链路的几次请求. 比如 servlet 服务器接收到用户请求, 调用 dubbo 服务, 然后将结果返回给用户, 整条链路只有一个 traceId. 开始于用户请求, 结束于用户收到结果.
spanId
一个链路中每次请求都会有一个 spanId. 例如一次 rpc, 一次 sql 都会有一个单独的 spanId 从属于 traceId.
cs
Clent Sent 客户端发起请求的时间, 比如 dubbo 调用端开始执行远程调用之前.
cr
Client Receive 客户端收到处理完请求的时间.
ss
Server Receive 服务端处理完逻辑的时间.
sr
Server Receive 服务端收到调用端请求的时间.
sr - cs = 请求在网络上的耗时
ss - sr = 服务端处理请求的耗时
cr - ss = 回应在网络上的耗时
cr - cs = 一次调用的整体耗时
Zipkin 的工作过程
当用户发起一次调用时, Zipkin 的客户端会在入口处为整条调用链路生成一个全局唯一的 trace id, 并为这条链路中的每一次分布式调用生成一个 span id.span 与 span 之间可以有父子嵌套关系, 代表分布式调用中的上下游关系. span 和 span 之间可以是兄弟关系, 代表当前调用下的两次子调用. 一个 trace 由一组 span 组成, 可以看成是由 trace 为根节点, span 为若干个子节点的一棵树.
Zipkin 会将 trace 相关的信息在调用链路上传递, 并在每个调用边界结束时异步的把当前调用的耗时信息上报给 Zipkin Server.Zipkin Server 在收到 trace 信息后, 将其存储起来. 随后 Zipkin 的 web UI 会通过 API 访问的方式从存储中将 trace 信息提取出来分析并展示.
二, Zipkin 的部署与运行
Zipkin 的 GitHub 地址: https://github.com/apache/incubator-zipkin
Docker 方式
docker run -d -p 9411:9411 openzipkin/zipkin
Jar 包方式 (JDK8)
- curl -sSL https://zipkin.apache.org/quickstart.sh | bash -s
- java -jar zipkin.jar
注意: 以上方式的 Zipkin 都是基于内存存储, Zipkin 重启后数据会丢失, 建议测试环境使用. Zipkin 支持的存储类型有 inMemory,MySQL,Cassandra, 以及 ElastiCSSearch 几种方式. 正式环境推荐使用 Cassandra 和 Elasticsearch.
三, Zipkin 与 Dubbo 和 Springmvc 的集成
上面我们搭建好了 Zipkin 服务器, 现在的任务就是如何把我们系统内产生的请求数据报送给 Zipkin 服务器, 以便在 UI 上渲染出来.
1. pom.xml
- <!-- 使用 okhttp3 作为 reporter -->
- <dependency>
- <groupId>io.zipkin.reporter2</groupId>
- <artifactId>zipkin-sender-okhttp3</artifactId>
- <version>2.8.2</version>
- </dependency>
- <!-- brave 对 dubbo 的集成 -->
- <dependency>
- <groupId>io.zipkin.brave</groupId>
- <artifactId>brave-instrumentation-dubbo-rpc</artifactId>
- <version>5.6.3</version>
- </dependency>
- <!-- brave 对 mvc 的集成 -->
- <dependency>
- <groupId>io.zipkin.brave</groupId>
- <artifactId>brave-instrumentation-spring-webmvc</artifactId>
- <version>5.6.3</version>
- </dependency>
2. application.YAML
- zipkin:
- url: http://127.0.0.1:9411/api/v2/spans
- connectTimeout: 5000
- readTimeout: 10000
- # 取样率, 指的是多次请求中有百分之多少传到 zipkin. 例如 1.0 是全部取样, 0.5 是 50% 取样
- rate: 1.0f
3. ZipkinProperties.java
- @Configuration
- @ConfigurationProperties("zipkin")
- public class ZipkinProperties {
- @Value("${spring.application.name}")
- private String serviceName;
- private String url;
- private Long connectTimeout;
- private Long readTimeout;
- private Float rate;
- /*getter and setter*/
- }
注意: 记得在 SpringBoot 的启动类上加上 @EnableConfigurationProperties 注解才能使 @ConfigurationProperties("zipkin") 生效哦!
4. ZipkinConfig.java
- @Configuration
- public class ZipkinConfig {
- @Autowired
- private ZipkinProperties zipkinProperties;
- /**
- * 为了实现 dubbo rpc 调用的拦截
- *
- * @return
- */
- @Bean
- public Tracing tracing() {
- Sender sender = OkHttpSender.create(zipkinProperties.getUrl());
- AsyncReporter reporter = AsyncReporter.builder(sender)
- .closeTimeout(zipkinProperties.getConnectTimeout(), TimeUnit.MILLISECONDS)
- .messageTimeout(zipkinProperties.getReadTimeout(), TimeUnit.MILLISECONDS)
- .build();
- Tracing tracing = Tracing.newBuilder()
- .localServiceName(zipkinProperties.getServiceName())
- .propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "shiliew"))
- .sampler(Sampler.create(zipkinProperties.getRate()))
- .spanReporter(reporter)
- .build();
- return tracing;
- }
- /**
- * MVC Filter, 为了实现 SpringMvc 调用的拦截
- * @param tracing
- * @return
- */
- @Bean
- public Filter tracingFilter(Tracing tracing) {
- HttpTracing httpTracing = HttpTracing.create(tracing);
- httpTracing.toBuilder()
- .serverParser(new HttpServerParser() {
- @Override
- public <Req> String spanName(HttpAdapter<Req, ?> adapter, Req req) {
- return adapter.path(req);
- }
- })
- .clientParser(new HttpClientParser() {
- @Override
- public <Req> String spanName(HttpAdapter<Req, ?> adapter, Req req) {
- return adapter.path(req);
- }
- }).build();
- return TracingFilter.create(httpTracing);
- }
- }
如此, 我们就把 Zipkin 和 Dubbo 以及 Springmvc 的集成做好了:
踩过的坑如下, 共勉:
Zipkin Server 一定要在调用后才会产生数据, 不会先把服务的信息注册上去.
MVC 的拦截, span 的名字是以请求方式命名的, 如下:
如果仍然想查看, 某个请求路径的调用情况呢?
GitHub 源代码: https://github.com/JMCuixy/dubbo-demo
来源: https://www.cnblogs.com/jmcui/p/10940372.html