## 概述
一个完整的微服务系统包含多个微服务单元,各个微服务子系统存在互相调用的情况,形成一个 调用链。一个客户端请求从发出到被响应 经历了哪些组件、哪些微服务、请求总时长、每个组件所花时长 等信息我们有必要了解和收集,以帮助我们定位性能瓶颈、进行性能调优,因此监控整个微服务架构的调用链十分有必要,本文将阐述如何使用 Zipkin 搭建微服务调用链追踪中心。
正如 Ziplin 官网 所描述,Zipkin 是一款分布式的追踪系统,其可以帮助我们收集微服务架构中用于解决延时问题的时序数据,更直白地讲就是可以帮我们追踪调用的轨迹。
Zipkin 的设计架构如下图所示:
要理解这张图,需要了解一下 Zipkin 的几个核心概念:
在某个应用中安插的用于发送数据给 Zipkin 的组件称为 Report,目的就是用于追踪数据收集
微服务中调用一个组件时,从发出请求开始到被响应的过程会持续一段时间,将这段跨度称为 Span
从 Client 发出请求到完成请求处理,中间会经历一个调用链,将这一个整个过程称为一个追踪( Trace )。一个 Trace 可能包含多个 Span,反之每个 Span 都有一个上级的 Trace。
一种数据传输的方式,比如最简单的 HTTP 方式,当然在高并发时可以换成 Kafka 等消息队列
看了一下基本概念后,再结合上面的架构图,可以试着理解一下,只有装配有 Report 组件的 Client 才能通过 Transport 来向 Zipkin 发送追踪数据。追踪数据由 Collector 收集器进行手机然后持久化到 Storage 之中。最后需要数据的一方,可以通过 UI 界面调用 API 接口,从而最终取到 Storage 中的数据。可见整体流程不复杂。
Zipkin 官网给出了各种常见语言支持的 OpenZipkin libraries:
本文接下来将 构造微服务追踪的实验场景 并使用 Brave 来辅助完成微服务调用链追踪中心搭建!
利用 Docker 来部署 Zipkin 服务再简单不过了:
docker run -d -p 9411:9411 \
--name zipkin \
docker.io/openzipkin/zipkin
完成之后浏览器打开:localhost:9411
可以看到 Zipkin 的可视化界面:
我们来构造一个如下图所示的调用链:
图中包含 一个客户端 + 三个微服务:
Client:使用 /servicea 接口消费 ServiceA 提供的服务
ServiceA:使用 /serviceb 接口消费 ServiceB 提供的服务,端口 8881
ServiceB:使用 /servicec 接口消费 ServiceC 提供的服务,端口 8882
ServiceC:提供终极服务,端口 8883
为了模拟明显的延时效果,准备在每个接口的响应中用代码加入 3s 的延时。
简单起见,我们用 SpringBt 来实现三个微服务。
ServiceA 的控制器代码如下:
@RestController
public class ServiceAContorller {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/servicea ”)
public String servicea() {
try {
Thread.sleep( 3000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
return restTemplate.getForObject("http://localhost:8882/serviceb", String.class);
}
}
ServiceB 的代码如下:
@RestController
public class ServiceBContorller {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/serviceb ”)
public String serviceb() {
try {
Thread.sleep( 3000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
return restTemplate.getForObject("http://localhost:8883/servicec", String.class);
}
}
ServiceC 的代码如下:
@RestController
public class ServiceCContorller {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/servicec ”)
public String servicec() {
try {
Thread.sleep( 3000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Now, we reach the terminal call: servicec !”;
}
}
我们将三个微服务都启动起来,然后浏览器中输入localhost:8881/servicea
来发出请求,过了 9s 之后,将取到 ServiceC 中提供的微服务接口所返回的内容,如下图所示:
很明显,调用链可以正常 work 了!
那么接下来我们就要引入 Zipkin 来追踪这个调用链的信息!
从 Zipkin 官网我们可以知道,借助 OpenZipkin 库 Brave,我们可以开发一个封装 Brave 的公共组件,让其能十分方便地嵌入到 ServiceA,ServiceB,ServiceC 服务之中,完成与 Zipkin 的通信。
为此我们需要建立一个新的基于 Maven 的 Java 项目:ZipkinTool
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.hansonwang99</groupId>
<artifactId>ZipkinTool</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>6</source>
<target>6</target>
</configuration>
</plugin>
</plugins>
</build>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<version>2.0.1.RELEASE</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.7.RELEASE</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-spring-web-servlet-interceptor</artifactId>
<version>4.0.6</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-spring-resttemplate-interceptors</artifactId>
<version>4.0.6</version>
</dependency>
<dependency>
<groupId>io.zipkin.reporter</groupId>
<artifactId>zipkin-sender-okhttp3</artifactId>
<version>0.6.12</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
其包含 endpoint 和 service 两个属性,我们最后是需要将该两个参数提供给 ServiceA、ServiceB、ServiceC 微服务作为其 application.properties 中的 Zipkin 配置
@Data
@Component
@ConfigurationProperties("zipkin")
public class ZipkinProperties {
private String endpoint;
private String service;
}
用了 lombok 之后,这个类异常简单!
[注意:关于 lombok 的用法,可以看这里]
这个类很重要,在里面我们将 Brave 的 BraveClientHttpRequestInterceptor 拦截器注册到 RestTemplate 的拦截器调用链中来收集请求数据到 Zipkin 中;同时还将 Brave 的 ServletHandlerInterceptor 拦截器注册到调用链中来收集响应数据到 Zipkin 中
上代码吧:
@Configuration
@Import({RestTemplate.class, BraveClientHttpRequestInterceptor.class, ServletHandlerInterceptor.class})
public class ZipkinConfiguration extends WebMvcConfigurerAdapter {
@Autowired
private ZipkinProperties zipkinProperties;
@Autowired
private RestTemplate restTemplate;
@Autowired
private BraveClientHttpRequestInterceptor clientInterceptor;
@Autowired
private ServletHandlerInterceptor serverInterceptor;
@Bean
public Sender sender() {
return OkHttpSender.create( zipkinProperties.getEndpoint() );
}
@Bean
public Reporter<Span> reporter() {
return AsyncReporter.builder(sender()).build();
}
@Bean
public Brave brave() {
return new Brave.Builder(zipkinProperties.getService()).reporter(reporter()).build();
}
@Bean
public SpanNameProvider spanNameProvider() {
return new SpanNameProvider() {
@Override
public String spanName(HttpRequest httpRequest) {
return String.format(
"%s %s",
httpRequest.getHttpMethod(),
httpRequest.getUri().getPath()
);
}
};
}
@PostConstruct
public void init() {
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
interceptors.add(clientInterceptor);
restTemplate.setInterceptors(interceptors);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(serverInterceptor);
}
}
ZipkinTool 完成以后,我们需要在 ServiceA、ServiceB、ServiceC 三个 SpringBt 项目的 application.properties 中加入 Zipkin 的配置:
以 ServiceA 为例:
server.port=8881
zipkin.endpoint=http://你 Zipkin 服务所在机器的 IP:9411/api/v1/spans
zipkin.service=servicea
我们最后依次启动 ServiceA、ServiceB、和 ServiceC 三个微服务,并开始实验来收集链路追踪数据 !
## 实际实验
浏览器打开 Zipkin 的 UI 界面,可以查看 依赖分析:
图中十分清晰地展示了 ServiceA、ServiceB 和 ServiceC 三个服务之间的调用关系! 注意,该图可缩放,并且每一个元素均可以点击,例如点击 ServiceB 这个微服务,可以看到其调用链的上下游!
接下来我们看一下调用链相关,点击 服务名,可以看到 Zipkin 监控到个所有服务:
同时可以查看 Span,如以 ServiceA 为例,其所有 REST 接口都再下拉列表中:
以 ServiceA 为例,点击 Find Traces,可以看到其所有追踪信息:
点击某个具体 Trace,还能看到详细的每个 Span 的信息,如下图中,可以看到 A B C 调用过程中每个 REST 接口的详细时间戳:
点击某一个 REST 接口进去还能看到更详细的信息,如查看 /servicec 这个 REST 接口,可以看到从发送请求到收到响应信息的所有详细步骤:
1
onepunch 2018-04-24 09:39:52 +08:00
40 分钟已经过去了,战略性 mark
|
2
byrain 2018-04-24 10:05:17 +08:00
嗯。这东西挺好的,我们这边做了 http grpc 以及 mysql 调用的追踪。
|
3
lawmil 2018-04-24 11:09:44 +08:00
战略 mark 一下
|
4
prolic 2018-04-24 11:22:06 +08:00
mark
|
5
bolide2005 2018-04-24 11:28:40 +08:00
建议再看看 OpenTracing,可以了解一些 Zipkin 更底层的设计思路
|
6
Mitt 2018-04-24 11:30:09 +08:00
mark
|
7
salmon5 2018-04-24 11:41:37 +08:00
|
8
hansonwang99 OP @salmon5 可以咨询一下 CEO
|
9
freehere 2018-04-24 11:49:21 +08:00
oneapm 是否直接能替代搭建这个系统?
|
10
projectzoo 2018-04-24 12:37:23 +08:00
看到“百度一下”在收藏夹第一位。
关了。 |
11
xuanyuanaosheng 2018-04-24 12:45:46 +08:00 via Android
mark,有同类产品比较么?其他产品有么?
|
12
tanszhe 2018-04-24 13:16:17 +08:00 1
一个日志分析就能有这个功能,还可以更细化 也不需要在应用中安插什么 Reporter,只要日志格式统一就好了。
实现这个主要就是给每个请求产生一个唯一 id |
13
flight2006 2018-04-24 13:33:57 +08:00 via Android
mark,公司有用过,但是没细细研究过
|
14
valkyrja 2018-04-24 14:16:40 +08:00 via iPhone 1
@projectzoo 一股恶臭的优越感扑面而来
|
15
RorschachZZZ 2018-04-24 14:43:59 +08:00
码
|
16
hpu423 2018-04-24 14:50:05 +08:00
以前公司搭建过,后来没推广成功
|
17
oswuhan 2018-04-24 16:38:38 +08:00
看开头不明觉厉,看完醍醐灌顶
|
18
YanY 2018-04-24 18:29:25 +08:00
@xuanyuanaosheng CAT 也是服务链调用日志
|
19
yuhuan66666 2018-04-24 18:37:24 +08:00 via Android
spring cloud 里的数据链路中心就是封装了这个
|
20
war1644 2018-04-24 20:33:46 +08:00
战略 mark 下,还在梳理公司 docker 微服务。调用链这块早晚得用上
|
21
PureWhite 2018-04-25 02:16:33 +08:00
其实就是分布式追踪
|
22
xuanyuanaosheng 2018-04-25 10:27:28 +08:00
@YanY 好的 谢谢
|
23
gmywq0392 2018-04-25 10:29:21 +08:00
现在主流的 APM 也有这种 trace 的功能了。比如 Elastic APM, Pinpoint, OneAPM 这些入侵性更少,而且从监控的角度来说体验更好呀。
|