You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
11 KiB
Markdown

2 years ago
# 16 | 如何搭建一套适合你的服务追踪系统?
[专栏第8期](http://time.geekbang.org/column/article/15273)我给你讲了服务追踪系统的原理以及实现,简单回顾一下服务追踪系统的实现,主要包括三个部分。
* 埋点数据收集,负责在服务端进行埋点,来收集服务调用的上下文数据。
* 实时数据处理负责对收集到的链路信息按照traceId和spanId进行串联和存储。
* 数据链路展示,把处理后的服务调用数据,按照调用链的形式展示出来。
如果要自己从0开始实现一个服务追踪系统针对以上三个部分你都必须有相应的解决方案。首先你需要在业务代码的框架层开发调用拦截程序在调用的前后收集相关信息把信息传输给到一个统一的处理中心。然后处理中心需要实时处理收集到链路信息并按照traceId和spanId进行串联处理完以后再存到合适的存储中。最后还要能把存储中存储的信息以调用链路图或者调用拓扑图的形式对外展示。
可以想象这个技术难度以及开发工作量都不小,对于大部分中小业务团队来说,都十分具有挑战。不过幸运的是,业界已经有不少开源的服务追踪系统实现,并且应用范围也已经十分广泛,对大部分的中小业务团队来说,足以满足对服务追踪系统的需求。
业界比较有名的服务追踪系统实现有阿里的鹰眼、Twitter开源的OpenZipkin还有Naver开源的Pinpoint它们都是受Google发布的Dapper论文启发而实现的。其中阿里的鹰眼解决方案没有开源而且由于阿里需要处理数据量比较大所以鹰眼的定位相对定制化不一定适合中小规模的业务团队感兴趣的同学可以点击本期文章末尾“拓展阅读”进行学习。
下面我主要来介绍下开源实现方案OpenZipkin和Pinpoint再看看它们有什么区别。
## OpenZipkin
OpenZipkin是Twitter开源的服务追踪系统下面这张图展示了它的架构设计。
![](https://static001.geekbang.org/resource/image/69/33/699916c60cd31a2b8d7ab0335038cf33.png)
(图片来源:[https://zipkin.io/public/img/architecture-1.png](https://zipkin.io/public/img/architecture-1.png)
从图中看OpenZipkin主要由四个核心部分组成。
* Collector负责收集探针Reporter埋点采集的数据经过验证处理并建立索引。
* Storage存储服务调用的链路数据默认使用的是Cassandra是因为Twitter内部大量使用了Cassandra你也可以替换成Elasticsearch或者MySQL。
* API将格式化和建立索引的链路数据以API的方式对外提供服务比如被UI调用。
* UI以图形化的方式展示服务调用的链路数据。
它的工作原理可以用下面这张图来描述。
![](https://static001.geekbang.org/resource/image/4c/d9/4c036659e0d14176215686f1a1129ed9.png)
(图片来源:[https://zipkin.io/pages/architecture.html](https://zipkin.io/pages/architecture.html)
具体流程是通过在业务的HTTP Client前后引入服务追踪代码这样在HTTP方法“/foo”调用前生成trace信息TraceIdaa、SpanId6b、annotationGET /foo以及当前时刻的timestamp1483945573944000然后调用结果返回后记录下耗时duration之后再把这些trace信息和duration异步上传给Zipkin Collector。
## Pinpoint
Pinpoint是Naver开源的一款深度支持Java语言的服务追踪系统下面这张图是它的架构设计。
![](https://static001.geekbang.org/resource/image/d8/a4/d8b526a56b633c34364924a2d00905a4.png)
(图片来源:[http://naver.github.io/pinpoint/1.7.3/images/pinpoint-architecture.png](http://naver.github.io/pinpoint/1.7.3/images/pinpoint-architecture.png)
Pinpoint主要也由四个部分组成。
* Pinpoint Agent通过Java字节码注入的方式来收集JVM中的调用数据通过UDP协议传递给Collector数据采用Thrift协议进行编码。
* Pinpoint Collector收集Agent传过来的数据然后写到HBase Storgage。
* HBase Storage采用HBase集群存储服务调用的链路信息。
* Pinpoint Web UI通过Web UI展示服务调用的详细链路信息。
它的工作原理你可以看这张图。
![](https://static001.geekbang.org/resource/image/87/95/8730864e70d666267e40e1cc4d622195.png)
(图片来源:[http://naver.github.io/pinpoint/1.7.3/images/td\_figure6.png](http://naver.github.io/pinpoint/1.7.3/images/td_figure6.png)
具体来看就是请求进入TomcatA然后生成TraceIdTomcatA^ TIME ^ 1、SpanId10、pSpanId-1代表是根请求接着TomatA调用TomcatB的hello方法TomcatB生成TraceIdTomcatA^ TIME ^1、新的SpanId20、pSpanId10代表是TomcatA的请求返回调用结果后将trace信息发给CollectorTomcatA收到调用结果后将trace信息也发给Collector。Collector把trace信息写入到HBase中Rowkey就是traceIdSpanId和pSpanId都是列。然后就可以通过UI查询调用链路信息了。
## 选型对比
根据我的经验,考察服务追踪系统主要从下面这几个方面。
**1\. 埋点探针支持平台的广泛性**
OpenZipkin和Pinpoint都支持哪些语言平台呢
OpenZipkin提供了不同语言的Library不同语言实现时需要引入不同版本的Library。
官方提供了C#、Go、Java、JavaScript、Ruby、Scala、PHP等主流语言版本的Library而且开源社区还提供了更丰富的不同语言版本的Library详细的可以点击[这里](https://zipkin.io/pages/existing_instrumentations)查看而Pinpoint目前只支持Java语言。
所以从探针支持的语言平台广泛性上来看OpenZipkin比Pinpoint的使用范围要广而且开源社区很活跃生命力更强。
**2\. 系统集成难易程度**
再来看下系统集成的难易程度。
以OpenZipkin的Java探针Brave为例它只提供了基本的操作API如果系统要想集成Brave必须在配置里手动里添加相应的配置文件并且增加trace业务代码。具体来讲就是你需要先修改工程的POM依赖以引入Brave相关的JAR包。
```
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-bom</artifactId>
<version>${brave.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
然后假如你想收集每一次HTTP调用的信息你就可以使用Brave在Apache Httpclient基础上封装的httpClient它会记录每一次HTTP调用的信息并上报给OpenZipkin。
```
httpclient =TracingHttpClientBuilder.create(tracing).build();
```
而Pinpoint是通过字节码注入的方式来实现拦截服务调用从而收集trace信息的所以不需要代码做任何改动。Java字节码注入的大致原理你可以参考下图。
![](https://static001.geekbang.org/resource/image/4a/75/4a27448c52515020c1f687e8e3567875.png)
(图片来源:[http://naver.github.io/pinpoint/1.7.3/images/td\_figure3.png](http://naver.github.io/pinpoint/1.7.3/images/td_figure3.png)
我来解释一下就是JVM在加载class二进制文件时动态地修改加载的class文件在方法的前后执行拦截器的before()和after()方法在before()和after()方法里记录trace()信息。而应用不需要修改业务代码只需要在JVM启动时添加类似下面的启动参数就可以了。
```
-javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar
-Dpinpoint.agentId=<Agent's UniqueId>
-Dpinpoint.applicationName=<The name indicating a same service (AgentId collection)
```
所以从系统集成难易程度上看Pinpoint要比OpenZipkin简单。
**3\. 调用链路数据的精确度**
从下面这张OpenZipkin的调用链路图可以看出OpenZipkin收集到的数据只到接口级别进一步的信息就没有了。
![](https://static001.geekbang.org/resource/image/33/23/33c924c5563be070416d8133e255af23.jpg)
(图片来源:[http://ovcjgn2x0.bkt.clouddn.com/zipkin-info.jpg](http://ovcjgn2x0.bkt.clouddn.com/zipkin-info.jpg)
再来看下Pinpoint因为Pinpoint采用了字节码注入的方式实现trace信息收集所以它能拿到的信息比OpenZipkin多得多。从下面这张图可以看出它不仅能够查看接口级别的链路调用信息还能深入到调用所关联的数据库信息。
![](https://static001.geekbang.org/resource/image/5f/3e/5f365d3c49cdb113bf6b08f5e3b36e3e.jpg)
(图片来源:[http://ovcjgn2x0.bkt.clouddn.com/pp-info.jpg](http://ovcjgn2x0.bkt.clouddn.com/pp-info.jpg)
同理在绘制链路拓扑图时OpenZipkin只能绘制服务与服务之间的调用链路拓扑图比如下面这张示意图。
![](https://static001.geekbang.org/resource/image/a7/e9/a7575c0826b77d236ddffe92d4d3c1e9.jpg)
(图片来源:[http://ovcjgn2x0.bkt.clouddn.com/zipdependency1.jpg](http://ovcjgn2x0.bkt.clouddn.com/zipdependency1.jpg)
而Pinpoint不仅能够绘制服务与服务之间还能绘制与DB之间的调用链路拓扑图比如下图。
![](https://static001.geekbang.org/resource/image/e5/1e/e59d46aa62e542246732ab9a985d281e.jpg)
(图片来源:[http://ovcjgn2x0.bkt.clouddn.com/ppreal.jpg](http://ovcjgn2x0.bkt.clouddn.com/ppreal.jpg)
所以从调用链路数据的精确度上看Pinpoint要比OpenZipkin精确得多。
## 总结
今天我给你讲解了两个开源服务追踪系统OpenZipkin和Pinpoint的具体实现并从埋点探针支持平台广泛性、系统集成难易程度、调用链路数据精确度三个方面对它们进行了对比。
从选型的角度来讲如果你的业务采用的是Java语言那么采用Pinpoint是个不错的选择因为它不需要业务改动一行代码就可以实现trace信息的收集。除此之外Pinpoint不仅能看到服务与服务之间的链路调用还能看到服务内部与资源层的链路调用功能更为强大如果你有这方面的需求Pinpoint正好能满足。
如果你的业务不是Java语言实现或者采用了多种语言那毫无疑问应该选择OpenZipkin并且由于其开源社区很活跃基本上各种语言平台都能找到对应的解决方案。不过想要使用OpenZipkin还需要做一些额外的代码开发工作以引入OpenZipkin提供的Library到你的系统中。
除了OpenZipkin和Pinpoint业界还有其他开源追踪系统实现比如Uber开源的Jaeger以及国内的一款开源服务追踪系统SkyWalking。不过由于目前应用范围不是很广这里就不详细介绍了感兴趣的同学可以点击“拓展阅读”自行学习。
## 思考题
OpenZipkin在探针采集完数据后有两种方式把数据传递给Collector一种是通过HTTP调用一种是基于MQ的异步通信方式比如使用RabbitMQ或者Kafka你觉得哪种方式更好一些为什么
欢迎你在留言区写下自己的思考,与我一起讨论。
* * *
**拓展阅读:**
阿里巴巴鹰眼:[http://ppt.geekbang.org/slide/download/939/595f4cdcb9d52.pdf/18](http://ppt.geekbang.org/slide/download/939/595f4cdcb9d52.pdf/18)
Jaeger[https://www.jaegertracing.io](https://www.jaegertracing.io)
SkyWalking[https://github.com/apache/incubator-skywalking](https://github.com/apache/incubator-skywalking)