微服务 - 链路追踪集成

Spring Cloud 集成 链路追踪


摘要

关于 链路追踪

​ 当我们在使用微服务时,会需要有这样的需求,当一个服务调用了另一个服务,另一个服务又调用了其他的服务,其他的服务又调用了更多的服务,这样一条无止境的调用链,若其中一个环节出现问题,整个请求就会崩塌,当我们去排查问题时,往往需要从调用链的源头开始找起,一步一步的深入排查,这是一件很费劲的事情。

​ 所以我们需要在日志中加入一个“ 标识 ”,来表示这是一个请求。

所需工具
Log4j2

​ Log4j2 在此作为日志输出的核心组件( 异步日志组件 )

Zipkin

​ Zipkin 在此作为链路追踪的核心服务,我们需要他做的事情其实只有一个,就是生成 “标识” 。

Sleuth

​ Sleuth 在此配合 Zipkin 服务,主要负责系统采样工作。

1. Log4j2

​ 首先,引入 Log4j2 的依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

<dependency>
	<groupId>com.lmax</groupId>
	<artifactId>disruptor</artifactId>
	<version>3.3.6</version>
</dependency>

注:disruptor是一个基于无锁化环形队列的高性能并发框架,log4j2就是借助它进行高性能日志异步输出的。

​ 接着,增加 log4j2.xml 文件来配置日志信息

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <!-- 输出控制台的配置 -->
        <Console name="console" target="SYSTEM_OUT">
        	<!-- 指定日志输出格式 【日期时间 等级 日志】 -->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
        </Console>

        <!-- 日志记录配置 -->
        <RollingFile name="wholeRolling" fileName="/home/msg.log" filePattern="/home/msg-%d{yyyy-MM-dd}.log">
        	<!-- 过滤掉一些等级的日志 -->
            <Filters>
                <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
                <ThresholdFilter level="debug" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>

            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
            </Policies>
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{TRACE_ID}] %-5level %class{36} %L %M - %msg%xEx%n"/>
            <DefaultRolloverStrategy max="99999">
                <Delete basePath="/home" maxDepth="2">
                    <IfFileName glob="msg-*.log"/>
                    <IfLastModified age="5d"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

        <!-- 日志记录配置 -->
        <RollingFile name="errorRolling" fileName="/home/error.log" filePattern="/home/error-%d{yyyy-MM-dd}.log">
            <Filters>
                <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
            </Filters>

            <Policies>
                <TimeBasedTriggeringPolicy interval="1"/>
            </Policies>
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{TRACE_ID}] %-5level %class{36} %L %M - %msg%xEx%n"/>
            <DefaultRolloverStrategy max="99999">
                <Delete basePath="/home" maxDepth="2">
                    <IfFileName glob="error-*.log"/>
                    <IfLastModified age="5d"/>
                </Delete>
            </DefaultRolloverStrategy>
        </RollingFile>

    </Appenders>
    
    <Loggers>
        <root level="info">
            <AppenderRef ref="console"/>
            <appender-ref ref="wholeRolling"/>
            <appender-ref ref="errorRolling"/>
        </root>

        <logger name="log" level="debug" additivity="false">
            <AppenderRef ref="console"/>
            <appender-ref ref="wholeRolling"/>
            <appender-ref ref="errorRolling"/>
        </logger>

        <logger name="com.xnyzc.lhy" level="debug" additivity="false">
            <AppenderRef ref="console"/>
            <appender-ref ref="wholeRolling"/>
            <appender-ref ref="errorRolling"/>
        </logger>

    </Loggers>
</Configuration>

​ 当新增了以上配置之后,此时,我们控制台输出的日志已经具备以下格式

2020-07-10 00:27:42.490 INFO xxx xxx

这里添加一个 Log4j2 的小插曲,如何使用 Log4j2 来添加一个请求标识

使用 Log4j2 生成请求标识

​ 首先,新增一个 Interceptor 类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

/**
 * @author cuixiaojian
 * @create 2020-07-10 00:57
 */
@Component
public class LogInterceptor extends HandlerInterceptorAdapter {

    private Logger log = LoggerFactory.getLogger(this.getClass());
    
    /**
     * 日志跟踪标识
     */
    private static final String TRACE_ID = "TRACE_ID";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
    
    	// 使用 UUID 生成标识
        String traceId = UUID.randomUUID().toString();
        
        if (StringUtils.isEmpty(MDC.get(TRACE_ID))) {
        	// MDC是log4j用于存储应用程序的上下文信息(context infomation),从而便于在log中使用这些上下文信息。
        	// 向 MDC 中添加一个标识信息
            MDC.put(TRACE_ID, traceId);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    	// 请求结束后,移除该标识
        MDC.remove(TRACE_ID);
    }
}

​ 接着,将此 Interceptor 类,加入 WebMVC 中,至此所有的请求都会通过 logInterceptor 类

@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
	@Override
    public void addInterceptors(InterceptorRegistry registry) {
    	// 在此重写方法中添加 logInterceptor 类
        registry.addInterceptor(logInterceptor);
    }
}

​ 最后,在 Log4j2.xml 中添加配置,指定日志输出格式

<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{TRACE_ID}] %-5level %class{36} %L %M - %msg%xEx%n"/>

​ 然后再当我们发起一个HTTP请求时候,控制台输出的日志具备以下格式

2020-07-10 00:27:40.277 [85b5cde89-25186da-9e63a944-3bcf2ee3] INFO xxx xxx

​ 其实不难看出,大括号中的 UUID 就是本次请求的唯一标识,当我们通过该 “标识” 去检索日志时,就会检索出该请求的所有日志,但是使用 Log4j2 生成的请求标识,他的作用域仅仅在一个服务中,换句话说就是在微服务中以这种方式生成 “标识” 不可行。因为当一个服务调用到另一个服务时,其实是发起了第二次请求,依然会被 mvc 拦截,生成新的请求 “标识”。

​ 因为 Log4j2 生成的请求标识不可行,所以我们需要一个作用在整个微服务系统之上的组件 - Zipkin

2. Zipkin

Zipkin 作为链路追踪的核心服务, Twitter 公司 为我们提供了各种各样的平台搭建方式。

  1. 将 Zipkin 集成到自己的微服务系统 Module 中;
  2. 使用 Jar 包启动 Zipkin 服务;
  3. 使用 docker-compose 搭建 Zipkin 服务;

我们简单介绍一、二种方式,使用第三种 Docker 方式搭建 Zipkin 平台

1. 将 Zipkin 集成到自己的微服务系统 Module 中
1. 在项目中创建一个 Module

2. 引入 Zipkin 依赖

<dependency>
  <groupId>io.zipkin.java</groupId>
  <artifactId>zipkin-autoconfigure-ui</artifactId>
</dependency>
 
<dependency>
  <groupId>io.zipkin.java</groupId>
  <artifactId>zipkin-server</artifactId>
</dependency>

3. 添加配置文件 yml 或 properties

server.port=9411
spring.application.name=zipkin
zipkin.storage.type=mem

4. 启动类添加 @EnableZipkinServer 注解

5. 启动 Zipkin 服务,访问 http://localhost:9411/zipkin/,可进入 Zipkin 控制台 UI 界面
2. 使用 Jar 包启动 Zipkin 服务
1. 官网 Jar 包下载地址:https://dl.bintray.com/openzipkin/maven/io/zipkin/zipkin-server/

2. 下载后执行如下命令启动服务,默认端口为 9411

java -jar zipkin-server-2.19.3-exec.jar

3. 或者我们也可以执行如下命令将 Zipkin 服务作为守护进程后台运行

nohup java -jar zipkin-server-2.19.3-exec.jar &

4. 访问 http://localhost:9411/zipkin/,可进入 Zipkin 控制台 UI 界面
3. 使用 docker-compose 搭建 Zipkin 服务

参考资料:

http://www.apgblogs.com/docker-compose-zipkin/

https://windmt.com/2018/04/24/spring-cloud-12-sleuth-zipkin/

https://www.jianshu.com/p/7d9ff93bc89e

首先,需要安装 Docker 和 Docker-compose

// 安装 Docker 社区版

1. 安装 Docker
yum install -y docker-ce-18.03.1.ce

2. 启动并加入开机启动
systemctl start docker
systemctl enable docker

3. 验证安装是否成功
docker version

// 安装 Docker-compose (下载时间较长)

1. 通过访问 https://github.com/docker/compose/releases/latest 得到最新的 docker-compose 版本

curl -L https://github.com/docker/compose/releases/download/1.23.2/docker-compose-`uname -s`-`uname -m` -o /usr/bin/docker-compose

2. 给 docker-compose 授权
chmod +x /usr/bin/docker-compose

接着即可以使用 Docker-compose

// 创建配置文件
version: '2' 
services:
  zipkin:
  	// 指定镜像
    image: openzipkin/zipkin
    // 容器名称
    container_name: zipkin
    // 配置参数(将链路信息保存至ES)
    environment:
      - STORAGE_TYPE=elasticsearch
      - ES_HOSTS=192.168.3.109:9200
      - ES_INDEX=zipkin
      - ES_INDEX_SHARDS=1
      - ES_INDEX_REPLICAS=1
    network_mode: host
    ports:
      # Port used for the Zipkin UI and HTTP Api
      - 9411:9411
      
// 进入 yml 配置文件所在目录,执行命令创建容器
docker-compose up -d

// 访问页面
http://localhost:9411/zipkin/

Zipkin 平台搭建成功之后,即可以配置与我们的微服务系统连接,这里就需要用到 Sleuth组件

3. Sleuth

1. 引入 Sleuth 依赖

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-sleuth</artifactId>
	<version>1.2.5.RELEASE</version>
</dependency>

2. 在 yml 或 properties 中配置

spring:
  sleuth:
    web:
      client:
        enabled: true
    sampler:
      # 将采样比例设置为 1.0,也就是全部都需要。默认是 0.1
      probability: 0.2
  zipkin:
  	# 指定了 Zipkin 服务器的地址
    base-url: http://localhost:9411/

至此,还差最后一步配置 Log4j2 ,链路追踪已经完成,启动项目,来查看日志信息

配置 Log4j2 的日志输出格式,添加 “[%X{X-B3-TraceId},%X{X-B3-SpanId}]”
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{X-B3-TraceId},%X{X-B3-SpanId}] %-5level %class{36} %L %M - %msg%xEx%n"/>

查看输出日志

​ 此时,我们的日志以具备链路信息,查看服务调用链后会发现,我们便可以通过 TraceId 来检索一个请求的调用链下的所有日志信息。

2020-07-10 00:27:40.277 [85b5cde8925186da,9e63a9443bcf2ee3] INFO xxx xxx
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值