Sleuth 入门教程 - 中级篇
大纲
Spring Cloud 与 SkyWalking
SkyWalking 介绍
SkyWalking 的概述
SkyWalking 创建于 2015 年,用于提供分布式追踪功能。从 5.x
开始,项目进化为一个完成功能的 APM 系统,被用于追踪、监控和诊断分布式系统,特别是使用微服务架构、云原生或容器技术。更多介绍可参考:SkyWalking 官网、SkyWalking GitHub 项目。
SkyWalking 的功能
SkyWalking 提供的主要功能如下:
- 分布式追踪和上下文传输
- 应用、实例、服务性能指标分析
- 根源分析
- 应用拓扑分析
- 应用和服务依赖分析
- 慢服务检测
- 性能优化
SkyWalking 的特性
SkyWalking 主要特性如下:
- 多语言探针或者类库
- Java 自动探针,追踪和监控程序时,不需要修改源码
- 社区提供的语言探针:.NET Core、Node.js
- 多种后端存储
- 支持 Elastic Search、H2
- 支持 OpenTrancing:Java 自动探针和 OpenTracing API 协同工作。
- 轻量级、完善的后端聚合和分析功能
- 现代化 WebUI
- 日志集成
- 应用、实例和服务的告警
- 支持接受其他跟踪器的数据格式:
- Zipkin JSON、Thrift、Protobuf v1 和 v2 格式,由 OpenZipkin 库提供支持
- Jaeger 采用 Zipkin Thrift 或 JSON v1/v2 格式
SkyWalking 整体架构
SkyWalking 的整体架构主要由四部分组成 Collector、Agent、Web、Storage,具体如下图所示。从上到下是应用级别的接入,可以使用 SDK 形式的接入,也使用非入侵性的 Agent 形式接入,Agent 负载将数据转化成 SkyWalking Trace 数据协议,通过 HTTP 或者是 gRPC 发送到 Collector,然后 Collector 对收集到的数据进行分析和聚合,最后存储到 ElasticSearch 或者 H2 中,一般情况下 H2 只用于测试,右边的 UI 是通过 HTTP + GrahQL 进行数据获取展示,大体的流程就是这样。
SkyWalking 实战案例
本节将演示 Spring Cloud 与 SkyWalking 的实战案例,该案例使用了两个核心服务(Consumer Service、Provider Service)、Zuul 网关、Eureka 作为注册中心。案例的调用流程是这样的,首先通过访问 Zuul 网关,然后再将请求转发到 Consumer Service,接着 Consumer Service 通过 Feign 远程调用 Provider Service,最后返回响应结果。具体的调用流程图如下所示:
1. 版本说明
在本案例中,使用各组件的版本如下所示:
组件 | 版本 |
---|---|
Spring Boot | 2.0.3 |
Spring Cloud | Finchley.RELEASE |
ElasticSearch | 5.6.10 |
SkyWalking | 5.0.0-GA |
2. 安装与启动 SkyWalking
3. 创建 Maven 父级 Pom 工程
在父工程里面配置好工程需要的父级依赖,目的是为了更方便管理与简化配置,具体 Maven 配置如下:
1 | <parent> |
4. 创建 Eureka 工程
创建 Eureka Server 的 Maven 工程,配置工程里的 pom.xml
文件,需要引入 spring-cloud-starter-netflix-eureka-server
1 | <dependencies> |
创建 Eureka Server 的启动主类,这里添加相应注解,作为程序的入口:
1 |
|
添加 Eureka Server 需要的 application.yml
配置文件到工程中
1 | server: |
5. 创建 Zuul 工程
创建 Zuul 的 Maven 工程,配置工程里的 pom.xml
文件,需要引入 spring-cloud-starter-netflix-zuul
。由于需要将服务注册到 Eureka Server,工程下的 pom.xml
文件还需要引入 spring-cloud-starter-netflix-eureka-client
。
1 | <dependencies> |
创建 Zuul 的启动主类,添加 @EnableDiscoveryClient
和 @EnableZuulProxy
注解,作为程序的入口:
1 |
|
提示
在主启动类上面的 @EnableDiscoveryClient
和 @EnableZuulProxy
注解,主要用于开启服务发现和 Zuul 代理转发的功能。
添加 Zuul 需要的 application.yml
配置文件到工程中
1 | server: |
提示
在上述配置内容中,添加了 Ribbon 和 Hystrix 相关配置是为了在首次请求时不会出现超时现象。
6. 创建 Provider 工程
为了测试 Feign 的 Web 服务客户端的功能,必须要有一个服务提供者。创建 Provider 的 Maven 工程后,由于需要将服务注册到 Eureka Server,工程下的 pom.xml
文件需要引入 spring-cloud-starter-netflix-eureka-client
1 | <dependencies> |
创建 Provider 的启动主类,添加注解 @EnableDiscoveryClient
,将服务注册到 Eureka Server:
1 |
|
在 application.yml
文件中指定服务名称(provider-service
)、注册中心地址与端口号:
1 | server: |
创建用于测试的 Controller 类:
1 |
|
7. 创建 Consumer 工程
创建 Consumer 的 Maven 工程,配置工程里的 pom.xml
文件,需要引入 spring-cloud-starter-openfeign
。由于需要从 Eureka Server 获取服务列表,即作为 Eureka 客户端,还需要引入 spring-cloud-starter-netflix-eureka-client
。
1 | <dependencies> |
创建启动主类,添加注解 @EnableFeignClients
、@EnableDiscoveryClient
1 |
|
创建服务接口类,用于通过 Feign 调用 Provider 服务:
1 |
|
创建用于测试的 Controller 类,使用多种方式调用 Provider 服务的那个自定义 API:
1 |
|
在 application.yml
文件中配置端口号、注册中心地址:
1 | server: |
8. 使用 Agent 启动服务和监控
在 官网 下载 SkyWalking 的 5.0.0-GA
版本,解压后得到里面的 agent
目录(如下),该目录是探针相关的。
1 | apache-skywalking-apm-incubating |
手动创建四个目录(如下),分别为 service-eureka、service-zuul、service-consumer、service-provider,主要用于存放被监控的 Jar 和 agent
目录,每个应用单独使用一个对应的 agent
目录进行启动。
1 | ├── service-consumer |
其中的 agent
目录是之前下载 SkyWalking 并解压后得到的,分别修改 agent/config
目录里面的 agent.config
文件,只需要修改 agent.application_ code
这一项配置,这个代表应用。每个配置文件对应改为:
1 | agent.application_code=service-eureka |
而服务应用是上面开发好的,通过 Maven 命令 maven package
将项目打包后得到可执行的 Jar。最后,通过下面的命令按顺序启动要监控的服务,具体如下:
1 | java -javaagent:/usr/local/skywalking/service-eureka/agent/skywalking-agent.jar -jar /usr/local/skywalking/service-eureka/eureka-service-1.0-SNAPSHOT.jar |
启动命令从上到下依次执行即可,所有应用启动完成后,通过 http://localhost:8080
可以访问 SkyWalking 的管理界面,默认登录的用户名 / 密码是 admin / admin
:
通过 Postman 等 HTTP 工具调用 http://127.0.0.1:7070/client/getInfo
接口后,此时观察 SkyWalking 首页最下面会出现一个调用过程:
更详细的调用信息可以切换顶部左侧菜单中的 Applicaiotn、Service、Topology、Trace 和 Alarm 等标签项,首先看一下 Application 展示的内容:
接下来看一下 Service 展示的内容:
接下来看一下 Toplogy 展示的内容(整个拓扑图):
提示
- 刷新 Toplogy 页面时,可能会先看到一个
service-eureka
的节点。重复刷新可能会出现serivce-consumer
指向service-provider
的请求,再请求一次,再刷新可能会出现user --> service-zuul --> service-consumer --> service-provider
的调用,为什么会出现这样的现象呢? - 这里出现延迟的原因和请求调用顺序有关系,请求调用是顺序开始,但完成却是倒序完成的。在每个应用中完成请求处理后,会发送监控信息到 Collector,然后 Collector 才能依次输出采集的结果,所以就出现这种延迟显示的情况。
9. 下载案例代码
- 完整的案例代码可以从 这里 下载得到。
Spring Cloud 与 Pinpoint
Pinpoint 介绍
Pinpoint 的概述
Pinpoint 是一个分析大规模分布式系统的平台,并提供处理大量跟踪数据的解决方案。它自 2012 年 7 月开发,并千 2015 年 1 月 9 日作为开源项目推出。Pinpoint 是由韩国人编写的著名的 APM (Application Performance Management) 系统。
Pinpoint 的特性
Pinpoint 的特点
- 分布式事务跟踪,跟踪跨分布式应用的消息。
- 自动检测应用拓扑,帮助开发者搞清楚应用的架构。
- 水平扩展以便支持大规模服务器集群。
- 提供代码级别的可见性以便轻松定位失败点和瓶颈。
- 使用字节码增强技术,添加新功能而无须修改代码。
Pinpoint 的优势
- 非入侵式:使用字节码增强技术,添加新功能而无须修改代码。
- 资源消耗:对性能的影响最小(资源使用量增加)
Pinpoint 的兼容性
下面对 Pinpoint 涉及的组件和存储方面的中间件进行版本对比。
Pinpoint 的核心模块
Pinpoint 主要包含以下 4 个架构模块:
- HBase(用于存储数据)。
- PinpointCollector(部署在 Web 容器上)。
- Pinpoint Web(部署在 Web 容器上)。
- Pinpoint Agent(附加到需要分析的 Java 应用程序)。
为了更好地说明 Pinpoint 中架构的模块,需要看详细的架构图(如下)。大体的流程是这样的:首先通过 Agent 收集调用应用的数据,将数据发送给 Collector,Collector 负责处理和分析数据,最后将数据存储到 HBase 中,可以通过 Pinpoint Web UI 查看已经分析好的调用分析数据。
Pinpoint 的数据结构
在 Pinpoint 中,数据结构的核心由 Span、Trace 和 TraceId 组成。
- Span:RPC(远程过程调用)跟踪的基本单位。它表示 RPC 到达时处理的工作,包含跟踪数据。为确保代码级别的可见性,Span 将子项标记为 SpanEvent,作为数据结构。每个 Span 包含一个 TraceId。
- Trace:一系列跨度;它由相关的 RPC(Spans)组成。同一个跟踪中的跨距共享相同的 TransactionId。Trace 通过 SpanIds 和 ParentSpanIds 排序为分层树状结构。
- TraceId:由 TransactionId、SpanId 和 ParentSpanId 组成的密钥集合。TransactionId 表示消息 ID,SpanId 和 ParentSpanId 都表示 RPC 的父子调用关系。
- TransactionId(TxId):来自单个事务的分布式系统发送 / 接收的消息的 ID;它必须在整个服务器组中是全局唯一的。
- SpanId:接收 RPC 消息时处理的作业的 ID;它是在 RPC 到达服务器节点时生成的。
- ParentSpanId(pSpanId):生成 RPC 的父 Span 的 SpanId。如果节点是事务的起始点,则不会有父跨度(对于这些情况,使用值
-1
来表示跨度是事务的根跨度。
提示
上图能够比较直观地说明这些 ID 结构直接的关系,同时可以很好地描述一个 Trace 的过程。
Pinpoint 与 Zipkin 对比
对比点 | Zipkin | Pinpoint | 说明 |
---|---|---|---|
技术点 | √ | Zipkin 依赖于 Spring 框架的 API 支持,监控内容和范围有限 Pinpoint 使用字节码注入技术,监控更为深入和广泛,比如可以看到调用了几次 Redis、哪几个微服务、MySQL 以及执行的 SQL 等,可以非常方便地追踪问题 | |
接入成本 | √ | Zipkin 需要对应用进行简单改造,包括 Sleuth 的引入和配置 Pinpoint 只需要在服务器打入探针,对业务没有任何改动 | |
扩展性 | √ | Zipkin 使用了 Spring Cloud 常用的 RESTful 协议,可以方便地进行扩展 Pinpoint 现在使用 gRPC 协议,也可以使用 Thrift 传输协议,出于传输数据量和性能的考虑 | |
兼容性 | √ | Zipkin 对 Spring Cloud 的兼容性更好,也得益于上面说的扩展性 | |
产品 | √ | 相比较而言,Pinpoint 可以说是一款更成熟的产品 | |
性能 | √ | 由于采集的数据范围不同,即使 Pinpoint 采用了高性能的传输协议,但是 Zipkin 的性能依然更高 |
Pinpoint 实战案例
由于篇幅有限,详细的实战案例代码可参考《重新定义 Spring Cloud 实战》的 第 16.7 小节(389 页)。