可观测性
FUST 框架提供了一套基于 OpenTelemetry 的可观测性组件,支持分布式追踪、服务调用监控和指标收集等功能,帮助开发者更好地了解和监控应用程序的运行状态。
依赖配置
根据使用场景,FUST 提供了不同的可观测性组件,开发者可以根据需要选择合适的组件进行集成。
Maven 依赖
核心依赖
<!-- 可观测性 API -->
<dependency>
<groupId>com.zhihu.fust</groupId>
<artifactId>fust-telemetry-api</artifactId>
</dependency>
<!-- 可观测性 SDK 实现 -->
<dependency>
<groupId>com.zhihu.fust</groupId>
<artifactId>fust-telemetry-sdk</artifactId>
</dependency>
框架集成
<!-- Spring MVC 集成 -->
<dependency>
<groupId>com.zhihu.fust</groupId>
<artifactId>fust-telemetry-spring-mvc</artifactId>
</dependency>
<!-- Redis Lettuce 客户端集成 -->
<dependency>
<groupId>com.zhihu.fust</groupId>
<artifactId>fust-telemetry-lettuce</artifactId>
</dependency>
<!-- MySQL 客户端集成 -->
<dependency>
<groupId>com.zhihu.fust</groupId>
<artifactId>fust-telemetry-mysql</artifactId>
</dependency>
Gradle 依赖
核心依赖
// 可观测性 API
implementation 'com.zhihu.fust:fust-telemetry-api'
// 可观测性 SDK 实现
implementation 'com.zhihu.fust:fust-telemetry-sdk'
框架集成
// Spring MVC 集成
implementation 'com.zhihu.fust:fust-telemetry-spring-mvc'
// Redis Lettuce 客户端集成
implementation 'com.zhihu.fust:fust-telemetry-lettuce'
// MySQL 客户端集成
implementation 'com.zhihu.fust:fust-telemetry-mysql'
Spring Boot 自动配置
如果你使用 FUST Boot,可以通过引入 fust-boot-starter
依赖来自动配置可观测性组件:
<dependency>
<groupId>com.zhihu.fust</groupId>
<artifactId>fust-boot-starter</artifactId>
</dependency>
特性概览
- 分布式追踪:基于 OpenTelemetry 的分布式追踪能力,支持服务调用链路追踪
- 服务调用监控:记录服务间调用情况,包括调用延迟、错误率等指标
- 自动集成:与 Spring MVC、Redis、MySQL 等组件的自动集成
- 性能指标收集:支持收集和导出应用程序性能指标
- SPI 扩展机制:通过 SPI 机制支持自定义扩展
- 上下文传播:支持跨进程、线程的上下文传播
初始化配置
手动初始化
在非 Spring Boot 应用中,需要手动初始化可观测性组件:
// 初始化环境
Env.init();
// 初始化遥测组件
TelemetryInitializer.init();
如果需要自定义遥测提供程序,可以通过 SPI 机制注册自定义的 TelemetrySdkProvider
实现:
public class CustomTelemetrySdkProvider implements TelemetrySdkProvider {
// 实现你的自定义遥测提供程序
}
然后在 META-INF/services/com.zhihu.fust.provider.TelemetrySdkProvider
文件中添加实现类的完全限定名。
Spring Boot 自动配置
在 Spring Boot 应用中,FUST 提供了自动配置,无需手动初始化:
- 引入
fust-boot-starter
依赖 - 应用程序启动时,
ApplicationPreparedListener
会自动初始化可观测性组件
使用方法
创建 Telemetry 实例
// 创建遥测实例,传入instrumentation名称作为标识
Telemetry telemetry = Telemetry.create("your-service-name");
使用 Tracer 创建追踪
// 获取 Tracer
Tracer tracer = telemetry.getTracer();
// 创建 Span
Span span = tracer.spanBuilder("your-operation-name")
.setSpanKind(SpanKind.CLIENT)
.startSpan();
try (Scope scope = span.makeCurrent()) {
// 在此范围内执行操作
// 当前上下文中可以访问这个 span
doSomething();
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR);
throw e;
} finally {
span.end(); // 结束 span
}
服务调用监控
FUST 提供了 ServiceMeter
用于监控服务调用情况:
// 创建服务调用监控
ServiceMeter meter = telemetry.createServiceMeter(ServiceMeterKind.CLIENT);
meter.setMethod("yourMethod"); // 设置调用方法名
meter.setTargetService("targetService"); // 设置目标服务名
meter.setTargetMethod("targetMethod"); // 设置目标方法名
try {
// 执行服务调用
callService();
} catch (Exception e) {
// 记录错误
meter.setError(e);
throw e;
} finally {
// 结束监控并提交指标
meter.end();
}
上下文传播
在分布式系统中,需要在服务间传播上下文信息:
// 获取文本映射传播器
TextMapPropagator propagator = telemetry.getTextMapPropagator();
// 将当前上下文注入到载体中(如HTTP请求头)
propagator.inject(Context.current(), headers, (carrier, key, value) ->
carrier.put(key, value));
// 从载体中提取上下文(在接收端)
Context extractedContext = propagator.extract(Context.root(), headers,
(carrier, key) -> carrier.get(key));
服务入口标记
记录服务入口信息:
// 获取当前服务入口
ServiceEntry entry = Telemetry.getServiceEntry();
// 创建新的服务入口
ServiceEntry newEntry = ServiceEntry.create("your-entry-point");
框架集成
Spring MVC 集成
FUST 提供了与 Spring MVC 的集成,自动为 Web 请求创建追踪并记录调用指标:
// 在 Spring MVC 配置类中注册拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SpringMvcTelemetryInterceptor())
.addPathPatterns("/**");
}
}
Redis Lettuce 集成
FUST 提供了与 Redis Lettuce 客户端的集成,自动为 Redis 操作创建追踪:
// 获取 Lettuce 遥测实例
Telemetry telemetry = LettuceTelemetry.getTelemetry();
MySQL 集成
FUST 提供了与 MySQL 的集成,通过 TracingQueryInterceptor
自动为 SQL 查询创建追踪:
在 MySQL JDBC URL 中添加查询拦截器:
jdbc:mysql://localhost:3306/yourdb?queryInterceptors=com.zhihu.fust.telemetry.mysql.TracingQueryInterceptor
或在 MySQL DataSource 配置中添加:
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
// ... 其他配置
dataSource.addDataSourceProperty("queryInterceptors",
"com.zhihu.fust.telemetry.mysql.TracingQueryInterceptor");
return dataSource;
}
配置示例
典型的初始化流程
非 Spring Boot 应用中的典型初始化流程:
public class Main {
public static void main(String[] args) {
// 初始化环境
Env.init();
// 初始化遥测组件
TelemetryInitializer.init();
// 初始化日志系统
ILoggingSystem.get().initialize();
// 应用程序代码...
}
}
Spring Boot 应用中的自动配置
Spring Boot 应用中,FUST 会自动配置可观测性组件,无需手动初始化:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
gRPC 服务集成示例
public class GrpcServer {
public static void main(String[] args) {
Env.init();
TelemetryInitializer.init();
ILoggingSystem.get().initialize();
int port = 8010;
GrpcServerBuilder builder = GrpcServerBuilder.builder(port);
builder.requestMonitor(reqLog -> {
long totalTimeMs = TimeUnit.NANOSECONDS.toMillis(reqLog.totalDurationNanos());
RequestHeaders headers = reqLog.requestHeaders();
log.warn("monitor|headers={} totalTimeMs={}ms", headers, totalTimeMs);
});
builder.addService(new YourServiceImpl())
.build()
.start()
.join();
}
}
高级特性
自定义 TelemetrySdkProvider
如果需要自定义可观测性配置,可以实现 TelemetrySdkProvider
接口:
public class CustomTelemetrySdkProvider implements TelemetrySdkProvider {
@Override
public SdkTracerProvider sdkTracerProvider() {
return SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(
OtlpGrpcSpanExporter.builder()
.setEndpoint("your-collector-endpoint")
.build())
.build())
.build();
}
@Override
public SdkMeterProvider sdkMeterProvider() {
return SdkMeterProvider.builder()
.registerMetricReader(PeriodicMetricReader.builder(
OtlpGrpcMetricExporter.builder()
.setEndpoint("your-collector-endpoint")
.build())
.build())
.build();
}
@Override
public SdkLoggerProvider sdkLoggerProvider() {
return null; // 可选实现
}
@Override
public TextMapPropagator textMapPropagator() {
return TextMapPropagator.composite(
W3CTraceContextPropagator.getInstance(),
W3CBaggagePropagator.getInstance());
}
}
自定义服务指标收集器
如果需要自定义服务指标收集方式,可以实现 ServiceMeterCollectorFactory
接口:
public class CustomServiceMeterCollectorFactory implements ServiceMeterCollectorFactory {
@Override
public ServiceMeterCollector create(String instrumentationName) {
return new CustomServiceMeterCollector(instrumentationName);
}
}
然后在 META-INF/services/com.zhihu.fust.telemetry.api.ServiceMeterCollectorFactory
文件中添加实现类的完全限定名。
最佳实践
- 合理命名:为服务、操作和指标使用清晰、一致的命名规范,便于后续分析和理解
- 适当粒度:选择合适的追踪粒度,过细会增加开销,过粗则无法有效定位问题
- 捕获异常:始终在捕获到异常时记录到追踪中,以便分析问题
- 增加上下文信息:在追踪中添加足够的上下文信息,如请求参数、用户ID等
- 控制数据量:避免在追踪中添加过大的数据,如完整的请求/响应体
故障排查
常见问题
追踪丢失:
- 检查是否正确调用了
span.end()
- 确认 OpenTelemetry 配置是否正确
- 验证收集器是否正常运行
- 检查是否正确调用了
上下文传播失败:
- 检查是否正确使用了
propagator.inject()
和propagator.extract()
- 确认传播的键值对是否被正确传递
- 检查是否正确使用了
指标收集不完整:
- 检查
ServiceMeter
的使用是否正确 - 确保在所有路径上都调用了
meter.end()
- 检查
性能考虑
- 采样率控制:对于高流量系统,考虑使用采样策略减少数据量
- 批处理导出:使用批处理方式导出追踪和指标,减少网络开销
- 缓冲区大小:合理配置缓冲区大小,避免内存压力
- 异步处理:使用异步处理减少对主线程的影响