九、Spring Cloud Sleuth 链路监控

官网:
Spring Cloud Sleuth
Zipkin

一、链路监控介绍

1. 简介

在互联网的发展中,随着用户量、并发量、数据量的增长,我们的架构也在逐渐演变: 单体架构 -> 集群 -> 垂直拆分 -> 水平拆分 -> SOA -> 微服务,而此时的一个客户端的请求,可能已经不一次简单的http通信。以用户下单为例,可能是经过了 网关路由、用户服务(注册中心、负载均衡)、订单服务(注册中心、负载均衡)、商品服务(注册中心、负载均衡)..等等。而此时如果出现问题,问题的定位也不再是直接去读取容器的日志,而是需要有一个监控整个请求链路、收集每次请求的耗时、响应情况等,并收集这些数据进行可视化展示的服务组件。

链路监控开源技术

  • Zipkin : 进行数据收集、查找、展现
  • Pinpoint
  • Skywalking
  • Cat
  • Spring Cloud Sleuth

实现逻辑
数据上报,每个集成了sleuth的节点将请求信息上报给 sleuth 服务。
监控服务进行数据收集,展示。

2. Spring Cloud Sleuth介绍

Spring Cloud Sleuth 提供了一套完整的服务跟踪的解决方案,在分布式系统中提供追踪解决方案并且兼容支持了zipkin。可以支持 链路追踪、性能分析、可视化。

术语(Terminology)

  • Trace:
    一系列spans组成的一个树状结构。TraceId标记一次完整的请求,可以看做一整个链路所有的请求统一的父Id
  • Span:
    基本工作单元,例如,在一个新建的span中发送一个RPC等同于发送一个回应请求给RPC,span通过一个64位ID唯一标识,trace以另一个64位ID表示,span还有其他数据信息,比如摘要、时间戳事件、关键值注释(tags)、span的ID、以及进度ID(通常是IP地址)。span在不断的启动和停止,同时记录了时间信息,当你创建了一个span,你必须在未来的某个时刻停止它。
  • Annotation:
    用来及时记录一个事件的存在,一些核心annotations用来定义一个请求的开始和结束

cs- Client Sent -客户端发起一个请求,这个annotion描述了这个span的开始
sr- Server Received -服务端获得请求并准备开始处理它,如果将其sr减去cs时间戳便可得到网络延迟
ss- Server Sent -注解表明请求处理的完成(当请求返回客户端),如果ss减去sr时间戳便可得到服务端需要的处理请求时间
cr- Client Received -表明span的结束,客户端成功接收到服务端的回复,如果cr减去cs时间戳便可得到客户端从服务端获取回复的所有所需时间

以具体的一张链路监控图为例:
service1 --调用--> service2; service2 --调用--> service3; service2 --调用--> service4

二、模拟下单进行链路监控

1. 模拟下单链路

首先我们需要创建一套比较完整的链路,然后在每个链路节点添加 Sleuth 进行链路的监控。
以用户购买商品下单为例,模拟下单流程,结合之前使用到的Spring Cloud Netflix组件完成下单场景:
对购物车内1到多件商品进行下单,访问下单请求,传入: 商品id、商品数量、用户id等。

 模拟下单请求: (下单做的三件事:a.锁库存 b.获取商品信息 c.生成订单)

1. 首先客户端发起下单服务,访问网关gateway

2. 网关路由,将下单请求通过负载均衡分发至订单服务 order-service  (eureka、openfeign、ribbon)

3. 订单服务 
  a. 负载均衡访问商品服务,锁定库存  goods-service
  b. 负载均衡访问商品服务,获取订单对应商品列表明细数据  goods-service
  c. 订单服务内部根据商品数据生成订单 order-service

具体项目架构不在此详述。

项目结构:

demo地址: 案例demo地址

测试结果:

2. 集成 Sleuth 进行链路监控

集成Spring Cloud Slueth+Zipkin实现链路监控,并分析链路监控中的一些关键指标和因素

a. 添加sleuth依赖

如果需要对整个订单链路进行监控,每个节点都需要引入 sleuth 依赖,我们这里对这条链路上的 gateway、order、goods服务添加sleuth组件依赖。

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <!--上报信息-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>
        

b. 启用sleuth日志

gateway服务日志配置:

logging:
  level:
    root: debug

订单、商品服务日志配置:

logging:
  level:
    org.springframework.cloud.sleuth: debug

c. 分析日志

之后,我们再次发起下单请求,观察后台日志:

网关服务日志:

2019-08-17 22:34:55.047 DEBUG [spring-cloud-gateway-8080,73bf090c1f6f8f6e,73bf090c1f6f8f6e,false] 14040 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping   : Route matched: order_route
2019-08-17 22:34:55.047 DEBUG [spring-cloud-gateway-8080,73bf090c1f6f8f6e,73bf090c1f6f8f6e,false] 14040 ... ... 
2019-08-17 22:34:55.047 DEBUG [spring-cloud-gateway-8080,73bf090c1f6f8f6e,73bf090c1f6f8f6e,false] 14040 ... ... 
... ...
... ...

订单服务日志: 通过feign发起了两次商品服务的调用


2019-08-17 22:34:55.057 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] c.s.i.w.c.f.TraceLoadBalancerFeignClient : Before send
2019-08-17 22:34:55.057 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] o.s.c.s.i.w.c.f.LazyTracingFeignClient   : Sending a request via tracing feign client [org.springframework.cloud.sleuth.instrument.web.client.feign.TracingFeignClient@153cc4c4] and the delegate [feign.Client$Default@2fabcbd1]
2019-08-17 22:34:55.058 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] o.s.c.s.i.w.c.feign.TracingFeignClient   : Handled send of NoopSpan(73bf090c1f6f8f6e/7e2603d8bb55ac66)
2019-08-17 22:34:55.088 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] o.s.c.s.i.w.c.feign.TracingFeignClient   : Handled receive of NoopSpan(73bf090c1f6f8f6e/7e2603d8bb55ac66)
2019-08-17 22:34:55.089 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] c.s.i.w.c.f.TraceLoadBalancerFeignClient : After receive
2019-08-17 22:34:55.091 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] c.s.i.w.c.f.TraceLoadBalancerFeignClient : Before send
2019-08-17 22:34:55.092 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] o.s.c.s.i.w.c.f.LazyTracingFeignClient   : Sending a request via tracing feign client [org.springframework.cloud.sleuth.instrument.web.client.feign.TracingFeignClient@153cc4c4] and the delegate [feign.Client$Default@2fabcbd1]
2019-08-17 22:34:55.093 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] o.s.c.s.i.w.c.feign.TracingFeignClient   : Handled send of NoopSpan(73bf090c1f6f8f6e/f43adf54d8533001)
2019-08-17 22:34:55.101 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] o.s.c.s.i.w.c.feign.TracingFeignClient   : Handled receive of NoopSpan(73bf090c1f6f8f6e/f43adf54d8533001)
2019-08-17 22:34:55.101 DEBUG [shen-mall-order-service,73bf090c1f6f8f6e,09950cf87d99f8a1,false] 14468 --- [nio-8082-exec-3] c.s.i.w.c.f.TraceLoadBalancerFeignClient : After receive

商品服务日志:

2019-08-17 22:34:55.060  INFO [shen-mall-goods-service,73bf090c1f6f8f6e,7e2603d8bb55ac66,false] 7364 --- [nio-8083-exec-9] c.b.g.g.feign.GoodsFeignClient           : begin GoodsFeignClient.decreaseStock: [ItemStockDto(itemId=100023501, num=3), ItemStockDto(itemId=100026701, num=2)]
2019-08-17 22:34:55.097  INFO [shen-mall-goods-service,73bf090c1f6f8f6e,f43adf54d8533001,false] 7364 --- [io-8083-exec-10] c.b.g.g.feign.GoodsFeignClient           : begin GoodsFeignClient.getItemsByIds: [100023501, 100026701]

可见,当我们配置了sleuth时,后台服务在记录日志时,会添加类似于[spring-cloud-gateway-8080,73bf090c1f6f8f6e,73bf090c1f6f8f6e,false]格式的日志,记录链路的情况。
其中格式为:
[appname,tranceId,spanId,true/false]
appname: 服务名
tranceId: 标记一次完整的请求,一整个链路所有的请求统一的父Id
spanId: 一次请求生成一个spanId
true/false: 是否需要把输出的数据展示到其它平台,上报信息。

Annonation: 标记
CS: Client Sent -> SR: Server Received -> SS: Server Sent -> CR: Client Received

有了 cs、sr、ss、cr这些标记,我们就会知道整个链路里的一个请求从CS客户端发起到SR服务端接接受的具体时间,两种相减就可得出网络通信时长;SR-SS时间可得出服务端执行逻辑所需时长..等
但这些需要手动计算,其实spring cloud已经提供了可视化的工具帮助我们监控,比如spring-cloud-starter-zipkin

3. 集成 Zipkin 实现链路数据可视化监控

a. 安装zipkin服务

首先参考官网文档,安装Zipkin

curl -sSL https://zipkin.io/quickstart.sh | bash -s

启用服务

java -jar zipkin.jar

此时访问9411端口 http://192.168.197.128:9411/zipkin/可以打开链路监控页面

b. 链路服务添加依赖配置

网关、订单、商品服务添加zip依赖

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zipkin</artifactId>
        </dependency>

网关、订单、商品服务添加配置

spring:
  ...
  ...
  sleuth:
    sampler:
      probability: 1.0 # sleuth取样概率  0.5代表50%
  zipkin:
    base-url: http://192.168.197.128:9411/

c. zipkin UI 监控

此时我们重发一次下单请求,会发现服务日志会变成这样的

2019-08-19 21:14:43.632 DEBUG [spring-cloud-gateway-8080,51ff0b531711ad0c,51ff0b531711ad0c,true] ...
2019-08-19 21:14:43.632 DEBUG [spring-cloud-gateway-8080,51ff0b531711ad0c,51ff0b531711ad0c,true] ...
2019-08-19 21:14:43.632 DEBUG [spring-cloud-gateway-8080,51ff0b531711ad0c,51ff0b531711ad0c,true] ...

最后一位已经变为true,表示监控已经上报。

打开 http://192.168.197.128:9411/zipkin/ 也可以看到链路跟踪请求

可以看到请求总耗时,每个节点请求的耗时,请求类型,CS、SR、SS、CR

  1. 链路里有Redis是因为网关gateway使用了redis做持久化操作,会从redis读取最新的网关数据;
  2. 并行执行 gateway发起 post请求,耗时 1.07s
  3. 网关路由至订单服务 orer-service,post请求
  4. 之后调用商品服务 goods-service, 先调用put请求扣减库存,之后调用 get请求查询商品明细
    和我们设计的链路一模一样,展示的非常清楚。首次请求耗时1s多是因为redis等的数据预热,之后订单请求基本上100ms左右。
    当然,如果服务链路之间出去请求异常,Zipkin UI内也会统计展示。

d. zipkin设计

zipkin本身包含

Collector : 收集模块,收集节点上报的数据进行验证
Storage : 进行数据的持久化模块,支持像mysql、es等,默认内存存储
Search : 搜索模块
UI : 页面展示模块
posted @ 2019-09-09 23:12  BigShen  阅读(874)  评论(0编辑  收藏  举报