Spring Cloud 系列之OpenFeign:(7)链路追踪sleuth+zipkin

传送门

Spring Cloud Alibaba系列之nacos:(1)安装

Spring Cloud Alibaba系列之nacos:(2)单机模式支持mysql

Spring Cloud Alibaba系列之nacos:(3)服务注册发现

Spring Cloud 系列之OpenFeign:(4)集成OpenFeign

Spring Cloud 系列之OpenFeign:(5)OpenFeign的高级用法

Spring Cloud 系列之OpenFeign:(6)OpenFeign的链路追踪

关于链路追踪

 在前面一节Spring Cloud 系列之OpenFeign:(6)OpenFeign的链路追踪中,通过MDC实现了一个简易的链路追踪服务:

里面对于链接追踪没有过多的讨论,所以这里还是要先对它是什么,有什么作用,常用实现方式再做一次探讨!

为什么需要链路追踪

链路追踪这个名词也是17年初的时候才接触,当时入职一家做互联网金融的公司(以前都是在传统软件公司做企业级金融系统开发)。公司据说不久前融资了一笔钱,招兵买马准备大干一场。那时候公司在推行微服务,框架就是Spring Cloud套件,也就接触了zipkin这个概念,当时很经过一番名词轰炸,还是带来了不小的震撼:微服务,Springboot,注册中心Eureka,配置中心Config,网关Zuul,断路器,负载Ribbon,以及链路追踪的Sleuth(Springboot3已经被Micrometer Tracing项目取代)甚至是JAVA8的Lambda,还入手了一本翟永超的《Spring Cloud微服务实战》。

 被名词轰炸的一个背后原因是在开发toB系统的那些年,系统基本都是单体的,所有的功能都在一个项目代码里面,包括前端/后端(那时候也不流行前后端分离):

  • 银行/机构的大部分管理系统(甚至核心系统)都是内网专线,即所谓的私部署,不像toC这样面对互联网开放
  • toB的系统并发虽然业务相对复杂,但并发不高,对分布式没有强烈的需求
  • 最后一点,不一味追求新技术,稳定成熟才是首要考虑需求

在单体系统时代出了问题或者要排查问题,基本在本系统排查就够了,一般不超出关联上下游2-3个系统:

  • 查询本系统的相关日志即可,排查比较方便,内容项也不多:应用日志/数据库
  • 或者人肉联系上下游系统相关负责人去查下,范围也不扩大

但是在分布式系统架构下,这个方式就有点困难了:

  • 随着系统的调用链路变长依赖系统增多,查询日志就显得不那么容易了:几个十几个系统要逐个排查日志,可能每个系统的日志规范还不一样,中间要是哪个漏了还查不出来,难以定位
  • 微服务架构下,多半组织架构也不一样了:会按照微服务来划分不同的开发团队,沟通成本几何增加,要是每个问题都人肉拉群处理,那很快就大家不用干活了

所以分布式系统架构对于统一的服务调用请求追踪就显示很有必要了:一个链路追踪需要达到至少2方面的要求:

  • 从调用过程角度看:从客户端发起请求开始,记录请求流经的每一个服务,直到向客户端返回响应为止
  • 从记录过程角度看:能够记录具体调用了哪些服务,以及调用的顺序、开始时点、执行时长等信息

什么是链路追踪

上面说了一个链路追踪需要达到至少2方面的要求,既要追踪每次调用完整的链路,又要记录链路的每个服务细节,而这个在专业术语上分别叫作"追踪-Trace""跨度-Span":

  • 一次完整的请求链路叫作Trace
  • 一个Trace包括多个Span,一个Span对应一个服务(或组件)
  • 通过Trace与对应的Span可以形成调用追踪记录树

Spring Cloud 系列之OpenFeign:(7)链路追踪sleuth+zipkin_第1张图片

Trace 和 Spans(图片来源于Dapper 论文) 

现在就先通过集成SpringCloud组件的Sleuth体验一下吧

集成Sleuth

这里还是以工程tsm为例来演示SpringCloud如何集成Sleuth!

maven依赖

因为是SpringCloud项目,所以在auth及cipher工程都引入:

        
            org.springframework.cloud
            spring-cloud-starter-sleuth
        

配置文件修改

修改application.properties文件,将系统日志请求调成DEBUG级别:

logging.level.org.springframework.web.servlet.DispatcherServlet=DEBUG

测试请求

单个服务

先启动nacos,再新增一个测试接口并启动cipher服务:

@RestController
@Slf4j
public class SleuthTestController
{
    
    @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
    public void echo(@PathVariable String str)
    {
        log.info("echo init begin..........tId:{}", MdcContext.getTraceId());
    }
}

然后发送一个请求进行测试:http://localhost:8082/echo/aa,观察打印的日志类似如下:

35:06.575 DEBUG [cipher-service,1091573eecf0928c,1091573eecf0928c,true] 5204 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : GET "/echo/aa", parameters={}
2023-08-19 21:35:06.596  INFO [cipher-service,1091573eecf0928c,1091573eecf0928c,true] 5204 --- [nio-8081-exec-1] c.t.t.c.controller.SleuthTestController  : echo init begin..........tId:26dee1ed-823a-4738-9040-4b400dce816c
2023-08-19 21:35:06.609 DEBUG [cipher-service,1091573eecf0928c,1091573eecf0928c,true] 5204 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed 200 OK

这里要说明一下格式:应用名称,TraceId,SpanId,是否输出信息用于zipkin等服务收集

Spring Cloud 系列之OpenFeign:(7)链路追踪sleuth+zipkin_第2张图片

 多个服务

先启动nacos,再新增一个测试接口并启动cipher服务:log服务远程调用cipher服务

    @Autowired
    private CipherFeignClient authFeignClient;   

    @RequestMapping(value = "/cipher/echo/{str}", method = RequestMethod.GET)
    public String logEcho(@PathVariable String str)
    {
        return authFeignClient.echo(str);
    }

然后发送一个请求进行测试:http://localhost:8082/cipher/echo/aa,观察打印的日志类似如下:

与MDC集成

前面一篇通过MDC实现了简单的链路追踪,如果应用使用了logback就可以自动从MDC里面获取sleuth的信息:

@Slf4j
public class MdcInterceptor implements HandlerInterceptor
{
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
    {
        // 获取tId
        String tId = MdcContext.getTraceId();
        
        // 将tId放入MDC中,方便日志输出
        MDC.put("TraceId", tId);
        
        // 从MDC中获取sleuth的traceId,spanId
        String traceId = MDC.get("traceId");
        String spanId = MDC.get("spanId");
        log.info("sleuth traceId:{}, spanId:{}", traceId, spanId);
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
    {
        // 清除线程上下文
        MdcContext.clear();
        
        // 清楚MDC内容
        MDC.clear();
    }
}

在日志配置文件中logback.xml,可以直接获取:

%X{X-B3-TraceId:-},%X{X-B3-SpanId:-}

The SLF4J MDC is always set and logback users immediately see the trace and span IDs in logs per the example shown earlier. Other logging systems have to configure their own formatter to get the same result. The default is as follows: logging.pattern.level set to %5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}] (this is a Spring Boot feature for logback users). If you do not use SLF4J, this pattern is NOT automatically applied.

搭配zipkin

上面通过sleuth实现了链路的追踪,但是sleuth有一个缺点就是没有可视化界面,它只是把追踪信息输出到日志中,这样就不方便直观的全链路查询排查问题。所以通常情况下会搭配zipkin来做数据展示,并可以将日志输出到redis等做持久化。

安装zipkin

首先安装zipkin服务端,对于java来说,官方提供了docker安装或者直接运行jar包的方式。

Spring Cloud 系列之OpenFeign:(7)链路追踪sleuth+zipkin_第3张图片

这里就通过直接启动jar的方式来测试(如果是windows的话可以直接通过maven下载依赖包)

下载完成之后,启动一下

java -jar zipkin-server-2.24.3-exec.jar

正常会输出下面的启动信息:

Spring Cloud 系列之OpenFeign:(7)链路追踪sleuth+zipkin_第4张图片

 访问一下zipkin显示下面表示运行成功,http://127.0.0.1:9411/zipkin/: 

Spring Cloud 系列之OpenFeign:(7)链路追踪sleuth+zipkin_第5张图片

应用集成zipkin

maven依赖

如果想要同时使用sleuth与zipkin,将上面的pom依赖修改如下:

 
    org.springframework.cloud
    spring-cloud-starter-zipkin

测试请求

还是以上面的多个服务调用为例,发送测试请求之后,再zipkin上面查看调用链路:

Spring Cloud 系列之OpenFeign:(7)链路追踪sleuth+zipkin_第6张图片

里面清晰的记录的请求接口,响应时间,及结果,可以点击SHOW查看详情,飘红的就是调用失败的,比如把远程服务cipher关闭导致失败的:

Spring Cloud 系列之OpenFeign:(7)链路追踪sleuth+zipkin_第7张图片

 而对于的成功的记录: 

Spring Cloud 系列之OpenFeign:(7)链路追踪sleuth+zipkin_第8张图片

配置zipkin

如果对于zipkin有定制化需要,可以通过进行配置或开发!

比如zipkin跟服务不在同一个服务,可以配置zipkin的地址:

If you want to find Zipkin through service discovery, you can pass the Zipkin’s service ID inside the URL, as shown in the following example for zipkinserver service ID:

spring.zipkin.baseUrl: https://zipkinserver/

By default, api path will be set to api/v2/spans or api/v1/spans depending on the encoder version. If you want to use a custom api path, you can configure it using the following property (empty case, set ""):

spring.zipkin.apiPath: v2/path2

To disable this feature just set spring.zipkin.discoveryClientEnabled to `false.

你可能感兴趣的:(SpringCloud,java)