主题 分布式系统 MySQL

一、Zipkin

1.1、简介

Zipkin 是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper 的论文设计而来,由 Twitter 公司开发贡献。其主要功能是聚集来自各个异构系统的实时监控数据,用来追踪微服务架构下的系统延时问题。

应用系统需要进行装备(instrument)以向 Zipkin 报告数据。Zipkin 的用户界面可以呈现一幅关联图表,以显示有多少被追踪的请求通过了每一层应用。

Zipkin 以 Trace 结构表示对一次请求的追踪,又把每个 Trace 拆分为若干个有依赖关系的 Span。在微服务架构中,一次用户请求可能会由后台若干个服务负责处理,那么每个处理请求的服务就可以理解为一个 Span(可以包括 API 服务,缓存服务,数据库服务以及报表服务等)。当然这个服务也可能继续请求其他的服务,因此 Span 是一个树形结构,以体现服务之间的调用关系。

Zipkin 的用户界面除了可以查看 Span 的依赖关系之外,还以瀑布图的形式显示了每个 Span 的耗时情况,可以一目了然的看到各个服务的性能状况。打开每个 Span,还有更详细的数据以键值对的形式呈现,而且这些数据可以在装备应用的时候自行添加。

从图中可以看出如下的调用关系:整个调用链中有两个微服务 service1 和 service2,在 10ms(相对时间点)的时候,service1 作为客户端向 service2 发送了一个请求(Client Send),之后 service2 服务于 19ms 的时候收到请求(Server Receive),并用了 12ms 的时间来处理,并于 31ms 时刻将数据返回(Server Send),最后 service1 服务于 1ms 以后接收到此数据(Client Receive),因此整个过程共耗时 22ms。图中还给出了 service1 访问 service2 服务前后 Http Client 连接池的状态信息。

1.2、架构

如图所示,Zipkin 主要由四部分构成:收集器、数据存储、查询以及 Web 界面。Zipkin 的收集器负责将各系统报告过来的追踪数据进行接收;而数据存储默认使用 Cassandra,也可以替换为 MySQL;查询服务用来向其他服务提供数据查询的能力,而 Web 服务是官方默认提供的一个图形用户界面。

而各个异构的系统服务向 Zipkin 报告数据的架构如下图。

1.3、运行

使用 Docker 运行 Zipkin 最为简单,其过程如下:

gitclone https://github.com/openzipkin/docker-zipkin
cd docker-zipkin
docker-composeup

这样启动,默认会使用 Cassandra 数据库,如果想改用 MySQL,可以换做以下命令启动:

docker-compose -f docker-compose.yml -f docker-compose-mysql.yml up

启动成功以后,可以通过 http:// :8080 来访问。具体获取 IP 地址的方法请参阅 Docker 的相关文档。

二、Brave

2.1、简介

Brave 是用来装备 Java 程序的类库,提供了面向 Standard Servlet、Spring MVC、Http Client、JAX RS、Jersey、Resteasy 和 MySQL 等接口的装备能力,可以通过编写简单的配置和代码,让基于这些框架构建的应用可以向 Zipkin 报告数据。同时 Brave 也提供了非常简单且标准化的接口,在以上封装无法满足要求的时候可以方便扩展与定制。

2.2、初始化

Brave 的初始化就是要构建 Brave 类的实例,该库提供了 Builder 类用来完成这件事情。

注:下文中约定,大写的 Brave 指该 Java 类库,而 Brave 类指 com.github.kristofa.brave.Brave 类型,而小写的 brave 指该类型的实例。

Brave.Builderbuilder = new Brave.Builder("serviceName");
Bravebrave = builder.build();

其中的 serviceName 是当前服务的名称,这个名称会出现在所有跟该服务有关的 Span 中。默认情况下,Brave 不会将收集到的监控数据发送给 Zipkin 服务器,而是会以日志的形式打印到控制台。如果需要将数据发送给服务器,就需要引入 HttpSpanCollector 类。当前版本(3.8.0)将这个类命名为 Collector,这个概念容易跟 Zipkin 自身的 Collector 相混淆,因此在 Issue #173 中官方建议将其更名为 Reporter,也就是说这个类是用来向 Zipkin 的 Collector 报告数据的。

使用 HttpSpanCollector 的方法如下:

Brave.Builderbuilder = new Brave.Builder("serviceName");
builder.spanCollector(HttpSpanCollector.create("http://localhost:9411",new EmptySpanCollectorMetricsHandler()));
Bravebrave = builder.build();

使用 HttpSpanCollector.create 方法可以创建该类的一个对象,第一个参数就是 Zipkin 服务的地址(默认部署时的端口为 9411)。

如果使用 Spring 的话,为了方便扩展,建议添加一个名为 ZipkinBraveFactoryBean 的类,其内容大致如下:

package net.tangrui.example.brave;// 省略所有的 importpublic class ZipkinBraveFactoryBean implements FactoryBean<Brave> {private final String serviceName;private final String zipkinHost;private Braveinstance;public void setServiceName(final String serviceName) {this.serviceName = serviceName;}public void setZipkinHost(final String zipkinHost) {this.zipkinHost = zipkinHost;}private void createInstance() {if (this.serviceName == null) {throw new BeanInitializationException("Property serviceName
must be set.");}Brave.Builderbuilder = new Brave.Builder(this.serviceName);if (this.zipkinHost != null && !"".equals(this.zipkinHost)) {builder.spanCollector(HttpSpanCollector.create(this.zipkinHost, new EmptySpanCollectorMetricsHandler()));}this.instance = builder.build();}@Overridepublic BravegetObject() throws Exception {if (this.instance == null) {this.createInstance();}return this.instance;}@Overridepublic Class<?> getObjectType() {return Brave.class;}@Overridepublic boolean isSingleton() {return true;}
}

然后只需要在 application-context.xml 配置文件中使用该 FactoryBean 就可以了:

<beanid="brave"class="net.tangrui.example.brave.ZipkinBraveFactoryBean"p:serviceName="serviceName"p:zipkinHost="http://localhost:9411"/>

2.3、装备标准的 Servlet 应用

Brave 提供了 brave-web-servlet-filter 模块,可以为标准的 Servlet 应用添加向 Zipkin 服务器报告数据的能力,需要做的就是在 web.xml 文件增加一个 BraveServletFilter。

不过这个 Filter 在初始化的时候需要传入几个参数,这些参数可以通过 brave 对象的对应方法获得,但是注入这些构造参数,最简单的办法还是使用 Spring 提供的 DelegatingFilterProxy。

在 web.xml 中添加如下内容(最好配置为第一个 Filter,以便从请求最开始就记录数据):

<filter><filter-name>braveFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class><init-param>    <param-name>targetFilterLifecycle</param-name>  <param-value>true</param-value></init-param>
</filter>
<filter-mapping><filter-name>braveFilter</filter-name><url-pattern>/*</url-pattern><dispatcher>REQUEST</dispatcher><dispatcher>FORWARD</dispatcher><dispatcher>INCLUDE</dispatcher><dispatcher>ERROR</dispatcher>
</filter-mapping>

然后在配置文件中添加以下内容(创建 brave Bean 的有关代码请参考上文):

<!-- 注意:这里的 id 要使用和 web.xml 中的 filter-name 同样的值 -->
<beanid="braveFilter"class="com.github.kristofa.brave.servlet.BraveServletFilter"><constructor-argvalue="#{brave.serverRequestInterceptor()}"/><constructor-argvalue="#{brave.serverResponseInterceptor()}"/><constructor-arg><beanclass="com.github.kristofa.brave.http.DefaultSpanNameProvider"/></constructor-arg>
</bean>

最后一个类 com.github.kristofa.brave.http.DefaultSpanNameProvider 存在于 brave-http 模块中。当使用 Maven 或 Gradle 来管理项目的话,brave-http 会随着 brave-web-servlet-filter 的引入被自动关联进来。

一切无误的话就可以启动服务。如果给定了 zipkinHost 参数,数据就会被发送到指定的 Zipkin 服务器上,然后可以在其 Web 界面上看到相关内容;否则会有类似如下的信息打印到系统控制台(做了格式美化):

{"traceId": "27bf14862307cd99","name": "post","id": "d79a683e2900c293","parentId": "27bf14862307cd99","timestamp": 1.463737111294e+15,"duration": 772000,"annotations": [{"endpoint": {"serviceName": "service1","ipv4": "172.20.13.41"},"timestamp": 1.463737111294e+15,"value": "cs"},{"endpoint": {"serviceName": "service1","ipv4": "172.20.13.41"},"timestamp": 1.463737112066e+15,"value": "cr"}],"binaryAnnotations": [{"key": "route.conn_manager_stats.after","value": "[leased: 1; pending: 0; available: 0; max: 1000]","endpoint": {"serviceName": "service1","ipv4": "172.20.13.41"}},{"key": "route.conn_manager_stats.before","value": "[leased: 0; pending: 0; available: 0; max: 1000]","endpoint": {"serviceName": "service1","ipv4": "172.20.13.41"}},{"key": "total.conn_manager_stats.after","value": "[leased: 1; pending: 0; available: 0; max: 1000]","endpoint": {"serviceName": "service1",    "ipv4": "172.20.13.41"}},{"key": "total.conn_manager_stats.before","value": "[leased: 0; pending: 0; available: 0; max: 1000]","endpoint": {"serviceName": "service1","ipv4": "172.20.13.41"}}]
}

2.3、装备 Spring MVC 应用

Brave 自带了 brave-spring-web-servlet-interceptor 模块,因此装备 Spring MVC 项目变得非常容易,只需要在配置文件中添加一些 interceptor 就好了:

<mvc:interceptors><beanclass="com.github.kristofa.brave.spring.ServletHandlerInterceptor"><constructor-argvalue="#{brave.serverRequestInterceptor()}"/><constructor-argvalue="#{brave.serverResponseInterceptor()}"/><constructor-arg><beanclass="com.github.kristofa.brave.http.DefaultSpanNameProvider"/></constructor-arg><constructor-argvalue="#{brave.serverSpanThreadBinder()}"/>
</bean>
</mvc:interceptors>

2.4、装备 MySQL 服务

brave-mysql 模块在 JDBC 驱动层面添加了一些拦截器,可以对 MySQL 的查询进行监控。在使用之前也需要通过 Spring 进行一下配置。

<bean
class="com.github.kristofa.brave.mysql.MySQLStatementInterceptorManagementBean" destroy-method="close"><constructor-argvalue="#{brave.clientTracer()}"/>
</bean>

该配置的目的是要给 MySQLStatementInterceptorManagementBean 类注入一个 ClientTracer 实例,这个实例会在后来的 MySQL JDBC 驱动的拦截器中被使用。初始化完成以后只需要在连接字符串中添加如下参数就可以了:

?statementInterceptors=com.github.kristofa.brave.mysql.MySQLStatementInterceptor&zipkinServiceName=myDatabaseService

其中的 zipkinServiceName 用来指定该 MySQL 服务的名称,如果省略的话,会默认以 mysql-${databaseName} 的形式来呈现。

这里需要特别说明一点,因为 MySQL 服务是跟 Java 服务分离的,因此上文初始化 brave 对象时提供的服务名称,并不适用于 MySQL 服务,所以才需要在这里另外指定。

可以看出,添加了 statement interceptor 之后,可以看到 service2 请求 MySQL 查询的起止时间,以及执行的 SQL 语句等信息。

三、总结

本文主要介绍了 Zipkin 服务和其 Java 库 Brave 的一些基本概念及原理,并且针对 Brave 开箱提供的一些装备组件进行了详细的使用说明。在后面进阶篇的文章中,会对如何扩展 Brave 以实现自定义监控信息的内容进行介绍,敬请期待!

使用 Zipkin 和 Brave 实现分布式系统追踪(基础篇相关推荐

  1. SpringCloud Sleuth + zipkin 实现微服务链路追踪功能

    一.微服务架构下的问题 在大型的微服务架构系统中,存在很多不同的微服务应用,不同的微服务有依赖着其他微服务,以及不同微服务有可能由不同的团队维护.那么在这种复杂的系统架构中,将会存在一些问题,比如: ...

  2. 分布式系统消息中间件——RabbitMQ的使用基础篇

    分布式系统消息中间件--RabbitMQ的使用基础篇 转载于:https://www.cnblogs.com/zhehan54/p/9679101.html

  3. 谷粒商城分布式基础篇1-个人版

    基础篇 1 项目简介 1.1 项目背景 1.2 电商模式 市面上有5种常见的电商模式 B2B.B2C.C2B.C2C.O2O 1.2.1 B2B 模式 B2B(Business to Business ...

  4. 谷粒商城基础篇(保姆级总结)

    谷粒商城基础篇 文章目录 谷粒商城基础篇 项目相关基础 知识介绍 微服务架构图和项目描述 **微服务划分图** Vrgrant systemctl命令 配置环境 Docker自启动命令 下载mysql ...

  5. 分布式基础篇--介绍

    一.分布式基础篇 1 项目简介 1.1 项目背景 1.2 电商模式 市面上有5种常见的电商模式 B2B.B2C.C2B.C2C.O2O 1.2.1 B2B 模式 B2B(Business to Bus ...

  6. 安排,谷粒商城java分布式开发基础篇高级篇与高可用集群架构篇2020

    来源: 来自网络,如侵权请告知博主删除????. 仅学习使用,请勿用于其他- 最近有小伙伴管我要分布式这类的项目,还有一些要商城的,还有要springboot,springcloud,k8s等,几乎涵 ...

  7. 学习笔记---程序员练级攻略(入门篇、修养篇、专业基础篇、软件设计篇、高手成长篇)

    根据极客时间 左耳朵耗子 整理,请忽略每一行最后的数字 文章目录 1. 入门篇 47 2. 修养篇 51 3. 专业基础篇 56 4. 软件设计篇 60 5. 高手养成 63 1. 入门篇 47 1. ...

  8. 鸟哥的Linux私房菜(基础篇)- 第十八章、认识系统服务 (daemons)

    第十八章.认识系统服务 (daemons) 最近升级日期:2009/09/14 在 Unix-Like 的系统中,你会常常听到 daemon 这个字眼!那么什么是传说中的 daemon呢?这些 dae ...

  9. 鸟哥的Linux私房菜(基础篇)- 第十六章、例行性工作排程 (crontab)

    第十六章.例行性工作排程 (crontab) 最近升级日期:2009/09/11 学习了基础篇也一阵子了,你会发现到为什么系统常常会主动的进行一些任务?这些任务到底是谁在配置工作的?如果你想要让自己设 ...

最新文章

  1. 设置oracle服务自动启动
  2. python asyncio回调函数_最近用 Python 的 asyncio,有好多不懂。。
  3. 在ASP.NET MVC中使用IIS级别的URL Rewrite
  4. VNC over reverse SSH
  5. ListCtrl添加右键菜单(ListCtrl类里编辑,给ListCtrl 发送NM_RCLICK消息)
  6. WebShop WebSocket server 和WebSocket客户端的一对多关系维护
  7. sql unicode转中文_SELECT语句《SQL必知必会》
  8. 解决VM 与 Device/Credential Guard 不兼容。在禁用 Device/Credential Guard 后,可以运行 VM 的方法
  9. Netty工作笔记0073---Neety的出站和入站机制
  10. 如何从0开始,搭建企业的实时数据中台?
  11. Spring 通知和顾问进行增强
  12. HTML常用的标签总结
  13. java 设置内存参数_Java虚拟机内存参数设置
  14. jfreeChart生成报表
  15. qq空间已删信息服务器,qq空间恢复已删除的说说方法
  16. 翻译:swift 5 iOS Accessibility从入门到精通
  17. vue在一个方法执行完后执行另一个方法
  18. 如何通过Darkvm注册阿里云国际版?
  19. 计算机考试照片识别,PS照片JPEGsnoop帮你鉴别
  20. 行业研究报告-全球与中国吊钩市场现状及未来发展趋势

热门文章

  1. 公开预算 publicize budget
  2. java调用扫描仪_通过Java调用Dynamsoft .NET TWAIN SDK控制扫描仪扫描文档
  3. 我国计算机发展优势,浅析我国计算机应用发展.doc
  4. 现代密码学之数字签名
  5. react 电话344格式
  6. 批量计算图片的ssim和psnr
  7. cs231n资源入口总结
  8. 安徽省计算机二级机试题库,2010安徽省计算机等级考试试题 二级ACCESS最新考试试题库...
  9. JS中或者(或 or ||)与并且(与 and )
  10. html5 xdwlnjs cn,最近需要调用一个网站的js,但是发现是加密的,有大佬来解密下吗?...