文章目录
- springboot + dubbo 整合 zipkin 实现链路追踪
- 示例说明
- zipkin 下载和启动
- pom 文件配置
- yml 文件配置
- 链路追踪配置类
- 日志文件配置
- 验证
springboot + dubbo 整合 zipkin 实现链路追踪
示例说明
本篇文章涉及三个微服务,分别为 dubbo-gateway,dubbo-alipay,dubbo-order,调用流程如下图所示:
zipkin 下载和启动
zipkin 下载地址为 https://repo1.maven.org/maven2/io/zipkin/zipkin-server/,选择 zipkin-server-x.x.x-exec.jar 下载到本地。
在 zipkin 下载目录下,打开命令行窗口,运行 java -jar zipkin-server-x.x.x-exec.jar 启动,启动成功后如下图:
浏览器输入 http://localhost:9411,如下图所示:
pom 文件配置
向以上三个服务的 pom 文件中导入链路追踪需要的 jar 包
<!--链路追踪-->
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-bom</artifactId>
<version>5.13.10</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-bom</artifactId>
<version>2.16.3</version>
<type>pom</type>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-dubbo</artifactId>
<version>5.13.10</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-spring-beans</artifactId>
<version>5.13.10</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-context-slf4j</artifactId>
<version>5.13.10</version>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-okhttp3</artifactId>
<version>2.16.3</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-spring-webmvc</artifactId>
<version>5.13.10</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-instrumentation-httpclient</artifactId>
<version>5.13.10</version>
</dependency>
yml 文件配置
向以上三个服务的 yml 文件中增加如下配置:
dubbo:
provider:
filter: tracing
consumer:
filter: tracing
zipkin:
enable: true
base:
url: http://127.0.0.1:9411/api/v2/spans # 注意,不可缺少 /api/v2/spans
链路追踪配置类
在以上三个微服务的 config 目录下创建链路追踪配置类,其内容如下:
package com.stylefeng.guns.alipay.config;
import brave.CurrentSpanCustomizer;
import brave.SpanCustomizer;
import brave.Tracing;
import brave.context.slf4j.MDCScopeDecorator;
import brave.http.HttpTracing;
import brave.propagation.B3Propagation;
import brave.propagation.ExtraFieldPropagation;
import brave.propagation.ThreadLocalCurrentTraceContext;
import brave.rpc.RpcTracing;
import brave.sampler.Sampler;
import brave.servlet.TracingFilter;
import brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import zipkin2.Span;
import zipkin2.codec.Encoding;
import zipkin2.reporter.AsyncReporter;
import zipkin2.reporter.Sender;
import zipkin2.reporter.okhttp3.OkHttpSender;
import javax.servlet.Filter;
/**
* @ClassName TracingConfig
* @Description TODO
* @Author 听秋
* @Date 2022/7/29 10:33
* @Version 1.0
**/
@Configuration
@Import(SpanCustomizingAsyncHandlerInterceptor.class)
public class TracingConfig implements WebMvcConfigurer {
@Bean
Sender sender(@Value("${zipkin.base.url}") String url) {
return OkHttpSender.newBuilder()
.encoding(Encoding.PROTO3)
.endpoint(url)
.build();
}
/**
* Configuration for how to buffer spans into messages for Zipkin
*/
@Bean
@ConditionalOnBean(Sender.class)
AsyncReporter<Span> spanReporter(Sender sender) {
AsyncReporter.Builder builder = AsyncReporter.builder(sender);
builder.queuedMaxSpans(50000);
builder.queuedMaxBytes(104857600);
return builder.build();
}
/**
* Controls aspects of tracing such as the service name that shows up in the UI
*/
@Bean
Tracing tracing(@Value("${dubbo.application.name}") String applicationName, @Value("${zipkin.enable:false}") Boolean enable, @Autowired(required = false) AsyncReporter spanReporter) {
Tracing.Builder builder = Tracing.newBuilder()
.localServiceName(applicationName)
.propagationFactory(ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "dubbo-alipay"))
.currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()
// puts trace IDs into logs
.addScopeDecorator(MDCScopeDecorator.create())
.build()
);
if (enable) {
builder.spanReporter(spanReporter);
builder.sampler(Sampler.ALWAYS_SAMPLE);
} else {
builder.sampler(Sampler.NEVER_SAMPLE);
}
return builder.build();
}
@Bean
SpanCustomizer spanCustomizer(Tracing tracing) {
return CurrentSpanCustomizer.create(tracing);
}
/**
* Decides how to name and tag spans. By default they are named the same as the http method
*/
@Bean
HttpTracing httpTracing(Tracing tracing) {
return HttpTracing.create(tracing);
}
@Bean
RpcTracing rpcTracing(Tracing tracing) {
return RpcTracing.create(tracing);
}
/**
* Creates server spans for http requests
*/
@Bean
Filter tracingFilter(HttpTracing httpTracing) {
return TracingFilter.create(httpTracing);
}
@Autowired
SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;
/**
* Decorates server spans with application-defined web tags
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(webMvcTracingCustomizer);
}
}
日志文件配置
在以上三个微服务的 resource 目录下创建 logback-spring.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<!-- <include resource="org/springframework/boot/logging/logback/base.xml"
/> -->
<contextName>Logback For Basic Platform</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<!-- 定义日志文件 输入位置 -->
<springProperty scope="context" name="logDir" source="custom.log.dir" defaultValue="C:/logback/gateway"/>
<!-- 定义日志文件 输出级别 -->
<springProperty scope="context" name="logLevel" source="custom.log.level" defaultValue="info"/>
<!-- 日志最大的历史 30天 -->
<springProperty scope="context" name="logMaxHistory" source="custom.log.max-history" defaultValue="180"/>
<!-- 控制台输出日志 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} 【%X{traceId},%X{spanId},%X{parentId}】 [%thread] %-5level %logger-%msg%n %ex{5}</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<!--文件日志, 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<fileNamePattern>${logDir}/%d{yyyy-MM-dd}/pf.%i.log.zip</fileNamePattern>
<maxFileSize>50MB</maxFileSize> <!-- 日志文件过大会使的编辑器打开非常慢,因此设置日志最大50MB -->
<!--日志文件保留天数-->
<maxHistory>${logMaxHistory}</maxHistory>
<totalSizeCap>10GB</totalSizeCap> <!-- 总日志大小 -->
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} 【%X{traceId},%X{spanId},%X{parentId}】 [%thread] %-5level %logger-%msg%n %ex{5}</pattern>
</encoder>
</appender>
<!-- 异步输出 -->
<appender name="dayLogAsyncAppender" class="ch.qos.logback.classic.AsyncAppender">
<includeCallerData>true</includeCallerData>
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<appender-ref ref="FILE"/>
</appender>
<!--专为 spring 定制
-->
<logger name="org.springframework" level="${logLevel}"/>
<!-- root级别 DEBUG -->
<root level="${logLevel}">
<!-- 控制台输出 -->
<appender-ref ref="STDOUT" />
<!-- 文件输出 -->
<appender-ref ref="dayLogAsyncAppender" />
</root>
</configuration>
验证
编写接口和方法验证以上三个微服务的链路追踪情况(此处省略)。
访问 http://localhost:9411/,将看到如下请求链路: