微服务(五)——分布式链路追踪(Spring Cloud Sleuth(跟踪原理、实践))、Spring Cloud Alibaba(Spring Cloud Nacos(注册 / 配置中心))、Spring Cloud Sentinel (限流工具(通过配置文件手动配置流控规则))
分布式链路追踪
一、Spring Cloud Sleuth
1、引言和介绍
当代的互联网的服务,通常都是用复杂的、大规模分布式集群来实现的。互联网应用构建在不同的软件模块集上,这些软件模块,有可能是由不同的团队开发、可能使用不同的编程语言来实现、有可能布在了几千台服务器,横跨多个不同的数据中心。因此,就需要一些可以帮助理解系统行为、用于分析性能问题的工具。
Spring Cloud Sleuth 的诞生便是为了帮助解决此类问题。在学习 Spring Cloud Sleuth 之前,我们需要了解一下什么是分布式链路跟踪,为什么我们需要分布式链路跟着,它解决了什么样的问题。在微服务架构中,服务被切割成了很多的微服务,这些微服务相互之间通过 Http 的方式交互。在一个小型的微服务架构中,一次请求会涉及十几次不同项目之间的调用,在一个中型的互联网公司,一次请求平均会几百次的调用请求,那在出现问题时我们可能需要了解这些问题:
- 如何快速发现问题
- 一次请求都调用了哪些服务
- 为什么请求这么慢,到底是哪里出现了问题
- 请求调用失败时,究竟是哪个服务调用失败了
按照传统的方式根据日志来跟踪,那么当项目出现问题时,可能需要运维查询上百台甚至上万台服务日志来定位,仅仅日志收集的工作量都是巨大的。哪怕使用了 ELK 套件解决日志的收集问题,但如果将这些日志进行关联、定位也是一个巨大的工作量。有更好的解决方案吗?这就涉及到了分布式链路跟踪的概念。
现今业界分布式服务跟踪的理论基础主要来自于 Google 的一篇论文《Dapper, a Large-ScaleDistributed Systems Tracing Infrastructure》。我们先来根据一张图来了解一下一次请求的调用:
图中 A-E 分别表示五个服务,用户发起一次 X 请求到前端系统 A,然后 A 分别发送 RPC 请求到中间层B 和 C,B 处理请求后返回,C 还要发起两个 RPC 请求到后端系统 D 和 E。
以上完整调用回路中,一次请求需要经过多个系统处理完成,并且追踪系统是持续跟踪到请求的每一步,也就是说分布式链路跟踪需要记录、跟踪一次请求的所有相关数据。在前端用户发起一次 X 请求的时候,就需要给这个请求生产一个唯一的 ID,在后面的所有请求调用中都需要带着这个 ID,最后根据这个 ID 将整个请求串联起来。
一个完成的分布式链路跟踪系统主要有三部分:数据收集、数据存储和数据展示。数据收集需要在调用的过程中,记录每一次请求的开始时间、结束时间、服务ID等其它相关数据;数据存储需要在短时间内快速存储大量的跟踪数据,并且需要满足快速检索的需求;数据展示,根据不同的维度以图形化的形式将收集的数据展示到页面,方便运营人员对问题进行分析、定位。
Spring Cloud Sleuth 属于分布式链路跟踪系统中数据收集的一个实现,它支持集成 Zipkin 等产品以图形化的方式展示分布式链路中收集的数据。
2、Spring Cloud Sleuth 介绍
Spring Cloud Sleuth 为 Spring Cloud 实现了分布式追踪解决方案。Spring Cloud Sleuth 的实现过程也是充分吸收借鉴了 Google Dapper 的思想,并且沿用了一些 Google Dapper 术语。
Spring Cloud Sleuth 为服务之间调用提供链路追踪。通过 Sleuth 可以很清楚的了解到一个服务请求经过了哪些服务,每个服务处理花费了多长。从而让我们可以很方便的理清各微服务间的调用关系。此外Sleuth 可以帮助我们:
- 耗时分析: 通过 Spring Cloud Sleuth 可以很方便的了解到每个采样请求的耗时,从而分析出哪些 服务调用比较耗时;
- 可视化错误: 对于程序未捕捉的异常,可以通过集成 Zipkin 服务界面上看到;
- 链路优化: 对于调用比较频繁的服务,可以针对这些服务实施一些优化措施。
3、Sleuth 相关术语
a、Trace
服务追踪的追踪单元是从客户发起请求(request)抵达被追踪系统的边界开始,到被追踪系统向客户返回响应(response)为止的过程,称为一个“trace”。Trace 由一组 Span 形成树状结构,例如,如果运行分布式大数据存储,则可能由 PUT 请求形成 trace。
b、Span
每个 Trace 中会调用若干个服务,为了记录调用了哪些服务,以及每次调用的消耗时间等信息,在每次调用服务时,埋入一个调用记录,称为一个“span”。 Span 是 Sleuth 的基本工作单元,若干个有序的Span 就组成一个 trace。Span 由唯一 64 位 ID 标识,还有另一个 64 位 ID 标识其所属的 Trace。Span 可以启动和停止,它们可以追踪自己的时间信息,创建 span 后,必须在将来的某个时刻停止它。
启动 Trace 的初始 span 称为 root span,该 span 的 ID 值等于 trace ID。
c、Annotation
Annotation 相当于 Span 记录的语法,描述 Span 现在所处的状态,它主要由四个概念:
- cs : Client Sent 客户端发送。表示一个 Span 的起始。
- sr : Server Received 服务端接收。表示服务端接收到请求,并开始处理。如果减去 cs 的时间戳, 则表示网络传输时长。
- ss : Server Sent 服务端完成请求处理,应答信息被发回客户端。如果减去 sr 的时间戳,则表示服 务端处理请求的时长。
- cr : Client Received 客户端接收。标志着 Span 的结束。客户端成功的接收到服务端的应答信息。 如果减去 cs的时间戳,则表示请求的响应时长。
4、记录过程
了解完这些概念之后,我们来看一下 Spring Cloud Sleuth 如何使用这些术语来完成一次 Trace 的记录。
图中可以看出请求涉及到四个服务,每一次的请求和响应都会产生一个 Span 状态,在 Span 中会存储Span id 和 Trace id 用来标记他们的所属关系,同时 Span 中会使用 Annotation 标记每一个 Span 当前的状态。通过以上信息的有序组合很直观的展示了一次请求(Trace)的调用过程。
图中标记的每种颜色表示一个 span(有七个 span — 从 A 到 G),Span 的格式如下:
Trace Id = X # 所属 Trace id
Span Id = D # 自身 id
Client Sent # 状态
5、跟踪原理
当我们项目中引入 spring-cloud-starter-sleuth 包后,每次链路请求都会添加一串追踪信息,格式是 [server-name, main-traceId,sub-spanId,boolean] 。
- server-name:服务结点名称;
- main-traceId:一条链路唯一的 ID,为 TraceID
- sub-spanId:链路中每一环的 ID,为 SpanID
- boolean:是否将信息输出到 Zipkin 等服务收集和展示。
这种机制是如何实现的呢?我们知道 Spring Cloud 微服务是通过 Http 协议通信的,所以 Sleuth 的实现也是基于 Http 的,为了在数据的收集过程中不能影响到正常业务,Sleuth 会在每个请求的 Header上添加跟踪需求的重要信息,例如:
X-A1-TraceId:对应 TraceID;
X-A1-SpanId:对应 SpanID;
X-A1-ParentSpanId:前面一环的 SpanID;
X-A1-Sampled:是否被选中抽样输出;
X-Span-Name:工作单元名称。
这样在数据收集时,只需要将 Header 上的相关信息发送给对应的图像工具即可,图像工具根据上传的数据,按照 Span 对应的逻辑进行分析、展示。
6、实践
a、前期准备
因为要用到这玩意,需要至少好几个服务,所以使用的还是前面的服务,前面的项目一直都是聚合项目。因为是使用前面的聚合项目,所以不需要再新建项目,只需要添加下面这个依赖即可,任何代码都不用写。
这里本人是在 storage、business 服务中添加下面这个依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
b、测试与日志信息
在 business 项目中,这里找了个以前的接口,然后打印日志,查看结果即可:
然后启动三个服务,eureka、storage、business,接着测试接口,这时候可以看到打印的日志信息跟以前都不同了:
可以看到上面后面有两个逗号,这里其实是一个数组;后面两个分别是 tranceID,和 spanID,不过目前看后面两项暂时还没有,因为还没有访问接口。
这时候再去访问接口,然后查看结果:
可以看到这时候就有内容了。如果调用其他的服务这里还是会变。也可以调用其他服务查看这里的 id 的异同。
通过上面这些信息就能追踪到服务的调用。
但是这些也是要一个个看,还是很麻烦,所以这里就有个工具,这个工具就是 Zipkin,这个到后面讲到 es 的时候再介绍。
SpringCloud Alibaba
那么什么是 Spring Cloud Alibab 呢?本文就来和大家聊一聊这个问题。
Spring Cloud Alibaba 是阿里巴巴公司提供的一套微服务开发的一站式解决方案,大家都知道,淘宝和天猫在最近几年的双十一活动中积累下来了大量的高并发经验与分布式数据处理经验,基于此,阿里巴巴为 Java 领域贡献了很多优质的开源项目,而 Spring Cloud Alibaba 就是其中之一。该项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
Spring Cloud Alibaba 可以非常方便的和我们现有的微服务项目整合,只需要通过少量的注解,我们就可以将我们现有的微服务项目接入到阿里巴巴的微服务解决方案体系中,进而使用阿里巴巴的中间件来增强我们的项目。
当前,Spring Cloud Alibaba 主要提供了如下功能:
从官方给出的这些功能列表中,我们可以看到,对于 Spring Cloud 原本就有的功能, Spring Cloud Alibaba 基本上都是支持的,除此之外,还提供了大量的组件,使我们的项目可以非常方便的整合到阿里云服务中。对于使用了阿里云服务的开发者来说,这无疑是具有吸引力的,如果没有使用阿里云服务,则忽略这些特性即可。
另一方面,Spring Cloud Alibaba 提供了如下组件:
这些组件基本上覆盖了我们开发中的大部分场景,而且如果想在 Spring Cloud 中集成 Spring Cloud Alibaba 这些组件,操作还是非常方便的,这个我们在后面会为大家介绍 Sentinel 和 Nacos 的使用。
二、Spring Cloud Nacos (注册 / 配置中心)
1、基本介绍
Nacos 其实就是个服务注册中心,不过是个二合一的功能,可以当服务注册中心,也可以当分布式配置中心,有这两个功能。当服务注册中心跟 eureka 功能差不多,当分布式配置中心跟 Spring Cloud Config 差不多。
Nacos 提供了动态服务发现、服务配置和服务管理功能,咋一看你可能会觉得这不就是 Eureka 或者Consul 的翻版嘛!这句话也没错,不过 Nacos 的功能可不止这些!用官方的话来说,Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
2、关键特性
那么 Nacos 都包含哪些基本特性呢?
-
服务发现和服务健康监测:Nacos 支持基于 DNS 和 RPC 的服务发现,并且提供了基于传输层和应用层的服务健康检查,自动剔除不健康的主机或者实例,Nacos 还提供了统一的健康检查仪表盘,使运维工程师可以快速发现问题。
-
动态配置服务,这个功能有点类似于我们前面学过的 Spring Cloud Config 的作用,不过这里Nacos 也提供了一样的功能。
-
动态 DNS 服务,动态 DNS 服务支持权重路由,这样开发者可以更容易地实现中间层负载均衡、更灵活的路由策略、流量控制以及数据中心内网的简单DNS解析服务。动态 DNS 服务还能让开发者通过 DNS 协议实现服务发现,这样可以避免耦合到厂商私有服务发现 API。
-
服务及其元数据管理,Nacos 能让开发者从微服务平台建设的视角管理数据中心的所有服务及元数据,包括管理服务的描述、生命周期、服务的静态依赖分析、服务的健康状态、服务的流量管理、路由及安全策略、服务的 SLA 以及最首要的 metrics 统计数据。
3、生态
使用 Nacos 简化服务发现、配置管理、服务治理及管理的解决方案,让微服务的发现、管理、共享、组合更加容易。
4、安装
安装方式主要给大家介绍两种,一种就是直接安装,另一种则是通过 Docker 来安装。
a、直接安装
首先我们来看看 Nacos 的安装步骤,我们可以下载源码包编译安装也可以直接下载官方编译好的压缩包,这里我们采用第二种方式,下载地址:
https://github.com/alibaba/nacos/releases/download/1.0.0/nacos-server-1.0.0.tar.gz
下载完成后,解压进入 bin 目录下,如果是 Windows 系统,直接双击 startup.cmd 启动项目,如果是Linux/Unix/Mac 系统,执行如下命令启动项目:
sh startup.sh -m standalone
b、docker 安装
使用 Docker 安装,比较容易,首先我们先用 wget 的方式下载相关脚本:
wget https://github.com/nacos-group/nacos-docker/archive/master.zip
下载完成后,解压,进入解压目录中,可以以单机的形式启动 nacos ,也可以以集群方式启动 nacos ,单机版启动命令如下:
docker-compose -f example/standalone-derby.yaml up
以集群形式启动 nacos 命令如下:
docker-compose -f example/cluster-hostname.yaml up
通过观察下载的 nacos-docker 中的 example 目录下的 standalone-derby.yaml 和 cluster hostname.yaml 脚本,发现两者都集成了 prometheus 和 grafana 进来。因此无论是集群版启动,还是单机版启动,启动成功之后,我们也都可以直接访问 Prometheus 和 Grafana 。关于 Prometheus和 Grafana 的用法,读者可以参考本专栏前面的文章。
运行完 Docker 命令后,会有一个下载过程,稍等片刻,Docker 版的 Nacos 就启动成功了。
5、nacos 作为配置中心
nacos 作为服务中心并不是像 eureka 那样随着 SpringBoot 启动而启动,而是作为一个单独的软件来启动,就是上面在 docker 中安装的那个玩意。其实这么多服务中心中只有 eureka 是作为 SpringBoot 工程来启动的,其他的服务中心都是单独的一个软件启动。
nacos 作为配置中心跟注册中心是可以共存的!
将 Nacos 作为配置中心,作用类似于之前讲过的 Spring Cloud Config ,但是很明显这里要比Spring Cloud Config 方便一些,因为配置中心+注册中心二合一,一个服务做两件事。而且都是可视化操作,我们就先来看一下配置中心要怎么使用。
a、前期准备
b、初看 nacos 作为配置中心的效果
c、nacos 服务搭建
这里的配置我们主要配置了三个东西,分别是 Data ID、Group 以及配置内容。
Data ID 的完成格式是(注意:这里取名是有讲究的):
${prefix}-${spring.profile.active}.${file-extension}
- ${prefix} 默认为 spring.application.name 的值,当然开发者也可以直接在 Spring Boot 项目中通过 spring.cloud.nacos.config.prefix 属性来指定。
- ${spring.profile.active} 表示当前项目所处的环境,即对应 Spring Boot 中的spring.profile.active 属性,这是可选的,如果这个选项省略了,对应的 - 也将不复存在。
- ${file-extension} 表示配置的数据格式。
那么这里举个例子:
这样就算是配置好一个配置文件了。
d、开始配置
到这就要去看配置文件的代码了:
bootstrap(因为是当注册中心来用,所以要在系统启动前就要去读取里面的配置,所以这里是在 bootstrap 里面来配。):
测试:
访问这个接口,能看到信息就说明成功。
6、nacos 作为注册中心
接下来我们再来看 Nacos 第二个使用场景,就是作为注册中心。扮演的角色相当于 Eureka。先来看Nacos 官网的一张架构图:
和我们之前使用 Eureka 或者 Consul 的架构基本一致,首先我们搭建 Nacos Server 作为服务注册中心,然后 生产者 注册到服务注册中心,消费者 再从服务注册中心获取到 生产者 的信息,然后 消费者 就可以通过 RestTemplate 等工具调用 生产者 了,就是这样一个简单的架构,我们来看下如何实现。
使用这个,调用的话依然可以通过 RedisTemplate 或者 OpenFeign 来调用。都无所谓,可以非常平顺的糅合进前面的项目中使用。
a、前期准备
nacos01:
这里使用这个 OpenFeign 来调用。
nacos02:
跟上面一样。
b、开始使用
01是提供服务的,读取配置中心的文件。02去调用01 。
(1)nacos01
application:
bootstrap:
(2)nacos02
application:
bootstrap:
service 层写一个接口类:
测试接口,如果能显示信息就没有问题。
c、代码配置别名
7、最后强调
其他东西的使用方式跟之前是一模一样的,只是单纯的注册方式变了,在这个 nacos 的网站填写配置信息就行,其他该怎么使用就还是怎么使用。
三、Spring Cloud Sentinel (限流工具)
1、简介
根据官方文档的介绍,Sentinel 被称作分布式系统的流量防卫兵,看到这个名字,我相信很多小伙伴都会想到 Hystrix ,可是前面的文章也已经说过,Hystrix 早已是明日黄花了,Netflix 都说了这个东西不再维护,因此在前面的文章中向大家介绍了 Spring Cloud 官方推荐的替代品 Resilience4j ,可是除了这个替代品 Resilience4j 之外, Sentinel 也是一个可以考虑的方案。
那么到底什么是 Sentinel 呢?它又有哪些特性呢?我们先来了解下。
Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。根据官方文档的介绍,Sentinel 主要有如下特征:
- 丰富的应用场景:Sentinel 承接了阿里巴巴近 10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:Sentinel 同时提供实时的监控功能。开发者可以在控制台中看到接入应用的单台机器秒级数据,甚至 500台以下规模的集群的汇总运行情况。
- 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC的整合。开发者只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。开发者可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
当然大家知道,现在不管玩什么,都讲究一个生态,没有生态,也就意味着其他的框架都不和你玩,那你这个框架的价值就会大打折扣!那么 Sentinel 的生态又是怎么样的呢?来看一张来自 Sentinel官方文档的生态图
从这张生态图中可以看到,Sentinel 对目前主流的服务都有很好的支持,都能方便的整合到其中,例如Spring Cloud、Redis、Nacos、Apollo、RocketMQ 等。
从整体上来说,Sentinel 可以分为两个部分:
- 核心库(Java 客户端)不依赖任何框架/库,能够运行于所有 Java 运行时环境,同时对 Dubbo /Spring Cloud
等框架也有较好的支持。 - 控制台(Dashboard)基于 Spring Boot 开发,打包后可以直接运行,不需要额外的 Tomcat 等应用容器。
好了,这是我们对 Sentinel 的简单介绍,接下来,我们就来通过一个简单的例子来感受下 Sentinel 的基本用法。
2、基本用法
a、仪表盘安装
为了方便看到 Sentinel 的工作效果,我们需要首先安装好 Sentinel 仪表盘,官方提供的 Sentinel 仪表盘就是一个普通的 Spring Boot 工程,我们先将之下载下来(怕有 bug 就去下正式发布版本,别下最新版本即可):
github 地址:
https://github.com/alibaba/Sentinel
下载下来之后,直接按照 Spring Boot 项目启动的套路来运行即可。( jar 包所在路径按住 shift 键右键点击在此打开 powerShell 窗口,输入命令启动即可。)
然后效果:
用户名密码默认是:sentinel
b、前期准备
因为后面使用的时候出了点问题,这里展示下依赖(可能要注意一下版本):
c、开始简单使用
接着随便写一个接口,然后启动项目,先别调用接口,先去查看网站:
这时候可以看到就有这些东西了。
接着去访问接口,然后再回来看看:
d、配置流量控制
这里我们配置 QPS 单机阈值为 5,表示1秒内可以处理 5 个请求,超过数量的请求将进行排队等待,等待超时时间是 1 s,即 1 s 之内还没处理,就会抛出超时。
快速失败的意思是:假设设置阈值为 5 ,那么如果来 10 个请求,前五个可以处理,后五个直接失败。
排队等待的意思是:假设设置为 2 s,如果 2 s 内还没有处理那么就请求失败。
这里点击新增后,流控规则就可以看到:
e、测试接口查看排队等待两秒的效果
这里用这个配置:
然后写个 for 循环,访问10 次:
效果,看时间:
确实是两秒。
3、通过配置文件手动配置流控规则
但是目前有个问题,就是每次重启原有的规则都会消失,需要重新配。那么就需要整合 nacos,在里面手动配置好流控规则。
还是这个项目,然后添加依赖:
然后在 application 里面配置下面信息,bootstrap 里面的配置可以全部不要。(下面这些数据可以存数据库中,也可以放到 nacos 中):
这里我们主要配置了四项,倒数第四项到倒数第二项分别表示 Nacos 的地址,Nacos 中 dataId 的值,Nacos 中groudId 的值。最后一项 rule-type 定义了规则类型,在 Sentinel 中,有不同的规则,例如热点参数限流、系统自适应限流等。
接着就是要到 sentinel 中用 Json 格式去配置:
这里的 dataId 和 Group 要和我们前面 application.properties 中配置的一致。至于配置内容,含义如下:
- resource:这个指资源名称,一般来说就是我们配置的某一个接口或者某一个方法。
- limitApp:流控针对的调用源,我们可以针对不同调用源给出不同的流控方案,default 则表示不区分调用源。
- grade:阈值类型,0 表示并发线程数,1 表示根据 QPS。
- count:单机阈值。
- clusterMode:是否集群
- strategy:流控模式
- controlBehavior:流控效果
实际上,这里的配置参数,和我们自己手动在 Sentinel 后台配置的参数一模一样!