从零开始,手把手教你用 Spring AI Alibaba + Micrometer + Zipkin 搭建完整的 AI 应用可观测体系。无需阿里云 ARMS,完全开源免费。
一、为什么要做这件事?
1.1 背景与痛点
当你把大模型能力接入生产环境后,很快就会遇到这些头疼的问题:
- AI 调用慢得像蜗牛:用户问一句话,等了 10 秒才有回应,到底卡在哪一步?
- 报错像黑盒 :API 调用失败了,日志里只有一行
500 Internal Server Error,根本不知道是大模型挂了、网络超时,还是参数传错了。 - 成本失控:每次调用花了多少钱?哪些接口被滥用?完全没有数据支撑。
这就是**可观测性(Observability)**要解决的问题------它让你像给系统装上了 X 光机,能透视每一次请求的完整生命周期。
1.2 方案对比:商业 vs 开源
| 维度 | 阿里云 ARMS(商业方案) | Micrometer + Zipkin(本方案) |
|---|---|---|
| 成本 | 按量计费,免费额度有限 | 完全免费,零成本 |
| 部署 | 需要阿里云账号,一键接入 | 自建 Zipkin,Docker 一键启动 |
| 接入方式 | Java Agent 无侵入挂载 | 依赖注入 + YAML 配置 |
| 数据归属 | 存储在阿里云 | 完全自主,可存本地/S3/ES |
| 功能覆盖 | 商业级完整功能(告警、拓扑图、智能分析) | 核心链路追踪 + 性能指标 |
| 学习曲线 | 低(托管服务) | 中等(需要理解原理) |
一句话建议:
- 如果你追求"开箱即用、功能全面、预算充足" → 选 ARMS。
- 如果你追求"零成本、数据自主、核心够用" → 选本方案。
二、技术架构全景
2.1 系统架构图
📊 观测数据后端
☁️ 外部 AI 服务
🚀 Spring Boot 应用 (Port: 8080)
👤 用户层
🔍 可观测性采集层(自动埋点)
🤖 Spring AI Alibaba 服务层
📡 REST 控制器层
自动创建Span
自动创建Span
自动创建Span
自动创建Span
异步上报
暴露指标端点
用户请求
🖱️ HTTP/REST
ChatController
💬 对话接口
ImageController
🎨 图片生成接口
EmbeddingController
📊 向量化接口
ToolController
🔧 工具调用接口
ChatClient
💬 流式对话
ImageModel
🖼️ 文生图
EmbeddingModel
📐 文本向量化
ToolCalling
🌦️ 天气查询工具
Micrometer
📈 性能指标采集
Brave Tracer
🔗 分布式链路追踪
Span上下文传播
🆔 TraceID传递
通义千问
DashScope API
天气数据 API
🌤️ 实时天气
Zipkin Server
📡 Port: 9411
链路可视化
/actuator
📊 指标数据
2.2 数据流向原理
当用户发起一次请求时,数据是这样流动的:
┌─────────────┐ ┌──────────────────────────────────┐ ┌─────────────┐
│ 用户请求 │────▶│ Spring Boot │────▶│ 通义千问API │
│ GET /chat │ │ ┌─────────┐ ┌──────────┐ ┌────┴──┐ │ │
└─────────────┘ │ │Controller│─▶│ChatClient│─▶│HTTP │ └─────────────┘
│ └────┬────┘ └────┬─────┘ │Client │
│ │ │ └────┬──┘
│ └────────────┴─────────────┘
│ │
│ ┌──────────▼──────────┐
│ │ Micrometer + Brave │ ◀── 自动拦截每个调用点
│ │ (创建Span/记录耗时) │
│ └──────────┬──────────┘
│ │
│ ┌──────────▼──────────┐
│ │ AsyncReporter │ ◀── 异步批量上报
│ │ (内存队列缓冲) │
│ └──────────┬──────────┘
└────────────────────┼──────────────────────────┘
│
▼
┌──────────────────────┐
│ Zipkin Server │
│ :9411 │
│ (存储 + 可视化) │
└──────────────────────┘
核心原理:Micrometer 在代码的关键路径上自动"打桩"(创建 Span),Brave 负责把这些桩点串联成一条完整的调用链,最后异步发送到 Zipkin 做存储和展示。
三、核心组件深度解析
3.1 依赖体系:它们各自扮演什么角色?
xml
<!-- ========== 基础框架 ========== -->
<!-- Spring Boot Web:提供 REST 接口能力 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Actuator:暴露 /actuator 健康检查和指标端点 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- ========== AI 能力 ========== -->
<!-- Spring AI Alibaba 通义千问集成:封装了 DashScope API 调用 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<!-- 天气工具:让 AI 能查询实时天气 -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-tool-calling-weather</artifactId>
</dependency>
<!-- ========== 可观测性核心(重点!) ========== -->
<!--
micrometer-tracing-bridge-brave 是什么?
Micrometer 是 Spring 的"度量抽象层",它定义了统一的 API,但不关心底层实现。
Brave 是 Twitter 开源的分布式追踪库,实现了 OpenZipkin 协议。
这个 bridge 就是"翻译官"------把 Micrometer 的调用翻译成 Brave 能理解的追踪指令。
类比:Micrometer 像 JDBC 接口,Brave 像 MySQL 驱动,bridge 就是 JDBC-MySQL 连接器。
-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
<version>1.3.4</version> <!-- 生产环境推荐稳定版 -->
</dependency>
<!--
zipkin-reporter-brave 是什么?
Brave 采集到了链路数据,但它不知道怎么送出去。
这个 reporter 就是"快递员"------把 Brave 的 Span 数据打包,通过 HTTP 发送到 Zipkin Server。
它内部使用了异步批量发送机制,不会阻塞你的业务线程。
-->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
<version>3.4.3</version>
</dependency>
依赖关系图谱:
外部服务
你的 Spring Boot 应用
自动埋点
HTTP
业务代码
Spring AI Alibaba
Micrometer Tracing API
bridge-brave
翻译层
Brave
追踪引擎
zipkin-reporter
发送器
DashScope API
Zipkin Server
9411
3.2 版本选择建议
⚠️ 避坑提醒 :原项目使用了 1.5.0-M2(Milestone 里程碑版),可能存在稳定性问题。
| 场景 | 推荐版本 | 说明 |
|---|---|---|
| 生产环境 | 1.3.4 |
稳定版,经过充分测试 |
| 想尝鲜 | 1.5.x |
新特性多,但可能有 Bug |
| 未来趋势 | micrometer-tracing-bridge-otel |
OpenTelemetry 是行业标准,兼容性更好 |
四、配置详解:每一行都在做什么?
4.1 application.yml 完整配置
yaml
spring:
application:
name: observability-models-dashscope # 服务名,会显示在 Zipkin 中
# ========== AI 核心配置 ==========
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY} # 从环境变量读取,安全!
# ========== 可观测性:AI 调用日志(开发调试利器) ==========
observations:
log-completion: true # 记录 AI 返回的完整内容
log-prompt: true # 记录发送给 AI 的提示词
# 作用:在日志里能看到 "你问了什么" 和 "AI 回了什么",排查问题时 priceless
# ========== 工具配置 ==========
alibaba:
toolcalling:
weather:
api-key: ${WEATHER_API_KEY} # 天气 API Key
enabled: true
# ========== ChatClient 观测(细粒度控制) ==========
chat:
client:
observations:
log-prompt: true # 记录 prompt
log-completion: true # 记录 completion
include-error-logging: true # 错误也记录
# ========== 向量存储观测 ==========
vectorstore:
observations:
log-query-response: true # 记录向量查询结果
# ========== 工具调用观测 ==========
tools:
observations:
include-content: true # 记录工具返回的内容
# ========== HTTP 超时配置 ==========
http:
client:
read-timeout: 60s # AI 调用可能慢,给足 60 秒
server:
port: 8080
# ========== Actuator 配置(指标暴露) ==========
management:
endpoints:
web:
exposure:
include: "*" # 暴露所有端点,生产环境建议限制
endpoint:
health:
show-details: always # 健康检查显示详细信息
# ========== 链路追踪核心配置 ==========
tracing:
sampling:
probability: 1.0 # 采样率:1.0 = 100%,每条都追踪
# 生产环境建议 0.1(10%),减少性能开销
zipkin:
tracing:
endpoint: http://localhost:9411/api/v2/spans # Zipkin 接收地址
4.2 配置设计哲学
| 配置块 | 解决的问题 | 类比 |
|---|---|---|
observations.* |
"AI 到底在干什么?" | 像飞机黑匣子,记录每次对话 |
tracing.sampling |
"性能 vs 完整性" 的平衡 | 像摄像头覆盖率,100% 无死角但费电 |
zipkin.tracing.endpoint |
"数据送到哪?" | 像快递地址,填错了就永远收不到 |
4.3 docker-compose.yaml:一键启动 Zipkin
yaml
services:
zipkin:
image: 'openzipkin/zipkin:latest'
ports:
- '9411:9411'
environment:
- STORAGE_TYPE=mem # 内存存储,重启数据丢失,适合开发
# 生产环境建议改为 elasticsearch/mysql
存储模式对比:
| 模式 | 适用场景 | 数据持久化 | 启动命令 |
|---|---|---|---|
mem(默认) |
开发测试 | ❌ 重启丢失 | docker run -p 9411:9411 openzipkin/zipkin |
elasticsearch |
生产环境 | ✅ 长期存储 | 需额外启动 ES |
mysql |
中小规模 | ✅ 简单可靠 | 需配置数据库连接 |
五、代码实战:四个核心接口
5.1 启动类:为什么这么简单?
java
@SpringBootApplication
public class ObservabilityApplication {
public static void main(String[] args) {
SpringApplication.run(ObservabilityApplication.class, args);
}
}
对比 ARMS 方案 :不需要手动创建 OpenTelemetry Bean!
原因 :Micrometer + Brave 的集成是全自动的。Spring Boot 的自动配置机制会在 classpath 检测到相关依赖后,自动装配好所有追踪组件。你只需引入依赖 + 写配置,"魔法"就生效了。
5.2 对话接口:ChatModelController
java
@RestController
@RequestMapping("/observability/chat")
public class ChatModelController {
private final ChatClient chatClient;
// 构造函数注入:Spring 会自动装配配置好的 ChatClient
public ChatModelController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
/**
* 流式对话接口
*
* 为什么用 Flux<String> 而不是 String?
* - Flux 是响应式流,支持 Server-Sent Events (SSE)
* - 大模型生成回答是一个字一个字"蹦"出来的,Flux 能让用户实时看到每个字
* - 如果不使用流式,用户要等 AI 全部生成完才能看到结果,体验很差
*/
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chat(@RequestParam(defaultValue = "hi") String prompt) {
return chatClient
.prompt(prompt) // 设置提示词
.stream() // 启用流式模式
.content(); // 只提取内容部分
}
}
追踪效果:当你调用这个接口后,Zipkin 中会自动生成一条 Trace,包含:
chatClient.prompt()的耗时- HTTP 请求到 DashScope 的网络耗时
- AI 生成响应的等待耗时
5.3 图片生成接口:ImageModelController(含优化)
java
@RestController
@RequestMapping("/observability/image")
@Slf4j
public class ImageModelController {
private final ImageModel imageModel;
private static final String DEFAULT_PROMPT = "一只可爱的柴犬在樱花树下";
public ImageModelController(ImageModel imageModel) {
this.imageModel = imageModel;
}
/**
* 文生图接口(优化版)
*
* 流程:提示词 → 调用 DashScope 生图 → 获取图片 URL → 代理返回图片二进制
*
* 为什么做代理?
* - DashScope 返回的是临时 URL,可能有过期时间
* - 直接代理可以让前端无感知,URL 永远有效
*/
@GetMapping("/generate")
public void image(
@RequestParam(defaultValue = DEFAULT_PROMPT) String prompt,
HttpServletResponse response) {
try {
// 1. 调用 AI 生成图片
ImageResponse imageResponse = imageModel.call(new ImagePrompt(prompt));
String imageUrl = imageResponse.getResult().getOutput().getUrl();
log.info("Generated image URL: {}", imageUrl);
// 2. 从 URL 读取图片并代理返回
URL url = URI.create(imageUrl).toURL();
try (InputStream in = url.openStream();
OutputStream out = response.getOutputStream()) {
response.setContentType(MediaType.IMAGE_PNG_VALUE);
// StreamUtils 是 Spring 的工具类,比手动 read/write 更高效
StreamUtils.copy(in, out);
}
} catch (Exception e) {
// 优雅降级:返回 500 并记录详细错误
log.error("Image generation failed for prompt '{}': {}", prompt, e.getMessage(), e);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
}
原代码问题 vs 优化:
| 问题 | 风险 | 优化方案 |
|---|---|---|
| 无异常处理 | 调用失败直接抛 500,用户体验差 | 添加 try-catch,记录日志 |
| 无参数化 | 只能生成固定图片 | 添加 @RequestParam 支持自定义提示词 |
| 资源未关闭 | InputStream 可能泄漏 |
使用 try-with-resources 自动关闭 |
| 无日志记录 | 出了问题无法追溯 | 添加 SLF4J 日志 |
5.4 向量化接口:EmbeddingModelController
java
@RestController
@RequestMapping("/observability/embedding")
public class EmbeddingModelController {
private final EmbeddingModel embeddingModel;
public EmbeddingModelController(EmbeddingModel embeddingModel) {
this.embeddingModel = embeddingModel;
}
/**
* 简单调用:使用默认模型和参数
*
* 什么是 Embedding?
* - 把文字转换成数字向量(比如 [0.1, -0.3, 0.8, ...])
* - 相似的文字会有相似的向量,计算机就能"理解"语义
* - 用途:语义搜索、推荐系统、RAG(检索增强生成)
*/
@GetMapping("/simple")
public Map<String, Object> embeddingSimple() {
float[] embeddings = embeddingModel.embed("hello world.");
return Map.of(
"vectorSize", embeddings.length,
"sample", Arrays.copyOfRange(embeddings, 0, 5), // 只展示前 5 个值
"message", "Embedding generated successfully"
);
}
/**
* 高级调用:指定模型和参数
*
* DashScope 提供多种 Embedding 模型:
* - text-embedding-v1:通用场景,1536 维
* - text-embedding-v2:效果更好,1536 维
* - text-embedding-v3:最新版,支持更多语言
*/
@GetMapping("/advanced")
public Map<String, Object> embeddingAdvanced() {
var request = new EmbeddingRequest(
List.of("Spring AI Alibaba 很好用"),
DashScopeEmbeddingOptions.builder()
.withModel(DashScopeModel.EmbeddingModel.EMBEDDING_V3.getValue())
.build()
);
float[] embeddings = embeddingModel.call(request).getResult().getOutput();
return Map.of(
"model", "text-embedding-v3",
"vectorSize", embeddings.length,
"message", "Advanced embedding with custom model"
);
}
}
5.5 工具调用接口:ToolCallingController
java
@RestController
@RequestMapping("/observability/tools")
public class ToolCallingController {
private final ChatClient chatClient;
public ToolCallingController(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
/**
* 工具调用演示:天气查询
*
* 执行流程(全自动):
* 1. 用户问:"杭州天气怎么样?"
* 2. AI 分析:需要查询天气 → 决定调用 getWeatherService 工具
* 3. Spring AI 自动调用工具,传入参数 "杭州"
* 4. 工具返回天气数据(温度、湿度等)
* 5. AI 基于数据生成自然语言回答:"杭州今天晴,25°C..."
*
* 在 Zipkin 中,你会看到一条完整的链路:
* Controller → ChatClient → [工具调用 Span] → 天气 API → AI 生成回答
*/
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chat(
@RequestParam(defaultValue = "how weather in hangzhou?") String prompt) {
return chatClient
.prompt(prompt)
.toolNames("getWeatherService") // 显式指定可用工具
.stream()
.content();
}
}
六、从零到运行:完整操作手册
6.1 前置条件检查
bash
# 一键检查环境
java -version && mvn -version && docker --version && docker-compose --version
| 依赖 | 最低版本 | 检查命令 | 不通过怎么办? |
|---|---|---|---|
| JDK | 17 | java -version |
安装 Eclipse Temurin 17 |
| Maven | 3.8 | mvn -version |
brew install maven 或官网下载 |
| Docker | 20.10 | docker --version |
安装 Docker Desktop |
| Docker Compose | 2.0 | docker-compose --version |
新版 Docker 已内置 |
6.2 获取 API Key
通义千问 API Key(必须):
- 访问 DashScope 控制台
- 点击「开通服务」→ 选择「通义千问」
- 创建 API Key,复制
sk-开头的字符串
天气 API Key(可选,测试 ToolCalling 需要):
- 阿里云市场搜索"天气 API",选免费版或付费版
- 或跳过此步骤,不测试天气接口即可
6.3 启动 Zipkin(30 秒搞定)
bash
# 方式一:Docker Compose(推荐,项目已自带配置)
cd /path/to/your/project
docker-compose up -d
# 方式二:一条命令(适合快速体验)
docker run -d --name zipkin -p 9411:9411 openzipkin/zipkin:latest
# 验证启动
curl http://localhost:9411 # 看到 HTML 页面即成功
docker logs zipkin # 查看启动日志
6.4 配置环境变量
bash
# 临时生效(当前终端)
export AI_DASHSCOPE_API_KEY="sk-xxxxxxxxxxxxxxxx"
export WEATHER_API_KEY="your-weather-api-key" # 可选
# 永久生效(推荐)
echo 'export AI_DASHSCOPE_API_KEY="sk-你的Key"' >> ~/.bashrc
echo 'export WEATHER_API_KEY="你的Key"' >> ~/.bashrc
source ~/.bashrc
6.5 构建与启动应用
bash
# 1. 进入项目目录
cd observability-example
# 2. 构建(跳过测试加速)
mvn clean package -DskipTests
# 3. 启动
java -jar target/observability-example-1.0.0.jar
# 启动成功标志:
# 2025-04-30T13:00:00.123 INFO --- Started ObservabilityApplication in 3.456 seconds
# 2025-04-30T13:00:00.789 INFO --- Application is running on port 8080
6.6 测试接口与验证链路
bash
# ========== 测试 1:流式对话 ==========
curl -N "http://localhost:8080/observability/chat?prompt=你好,请介绍一下自己"
# 预期:逐字返回 AI 回复,像打字机效果
# ========== 测试 2:图片生成 ==========
curl "http://localhost:8080/observability/image/generate?prompt=一只在月球上弹吉他的猫" -o moon-cat.png
# 预期:生成 moon-cat.png 图片文件
# ========== 测试 3:向量化 ==========
curl "http://localhost:8080/observability/embedding/simple"
# 预期:{"vectorSize":1536,"sample":[0.12,-0.05,...]}
# ========== 测试 4:工具调用(天气) ==========
curl -N "http://localhost:8080/observability/tools?prompt=杭州今天天气怎么样"
# 预期:AI 自动调用天气工具,返回带实时数据的自然语言回答
6.7 查看 Zipkin 链路
-
打开浏览器访问 http://localhost:9411
-
点击「查找」按钮(不填条件查全部)
-
你应该看到类似这样的链路:
Trace: a1b2c3d4e5f6... Duration: 2.345s Spans: 5
├─── [0.000s - 2.345s] GET /observability/chat (Controller)
│
├─── [0.023s - 2.310s] ChatClient.call() (AI 调用)
│ ├─── [0.100s - 0.150s] HTTP POST /api/v1/services/aigc/... (请求 DashScope)
│ ├─── [0.800s - 2.100s] AI Generation (模型生成耗时)
│ └─── [2.100s - 2.200s] Response Parse (解析响应)
│
└─── [2.200s - 2.345s] Stream Response (流式返回)
关键指标解读:
| Span 名称 | 正常耗时 | 如果异常高说明 |
|---|---|---|
HTTP POST ... |
50-200ms | 网络问题或 DashScope 服务端慢 |
AI Generation |
1-5s | 模型复杂度高或并发大 |
Response Parse |
<50ms | 数据包太大或解析逻辑复杂 |
七、生产环境部署
7.1 Docker 部署(推荐)
dockerfile
# ========== 构建阶段 ==========
FROM eclipse-temurin:17-jdk-alpine AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
# 安装 Maven 并构建
RUN apk add --no-cache maven && mvn clean package -DskipTests
# ========== 运行阶段 ==========
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/observability-example-1.0.0.jar app.jar
# 环境变量(运行时注入)
ENV AI_DASHSCOPE_API_KEY=""
ENV WEATHER_API_KEY=""
ENV ZIPKIN_ENDPOINT="http://zipkin:9411/api/v2/spans"
# JVM 调优参数
ENV JAVA_OPTS="-XX:+UseG1GC -XX:MaxRAMPercentage=75.0 -XX:InitialRAMPercentage=50.0"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
构建与运行:
bash
# 构建镜像
docker build -t spring-ai-observability:1.0.0 .
# 运行(连接外部 Zipkin)
docker run -d -p 8080:8080 -e AI_DASHSCOPE_API_KEY=sk-xxxxx -e ZIPKIN_ENDPOINT=http://your-zipkin-host:9411/api/v2/spans --name ai-app spring-ai-observability:1.0.0
7.2 Kubernetes 部署
yaml
# ========== configmap.yaml ==========
apiVersion: v1
kind: ConfigMap
metadata:
name: ai-observability-config
data:
AI_DASHSCOPE_API_KEY: "sk-xxxxxxxxxxxxxxxx"
WEATHER_API_KEY: "your-key"
ZIPKIN_ENDPOINT: "http://zipkin.monitoring.svc.cluster.local:9411/api/v2/spans"
SPRING_PROFILES_ACTIVE: "prod"
---
# ========== deployment.yaml ==========
apiVersion: apps/v1
kind: Deployment
metadata:
name: ai-observability
labels:
app: ai-observability
spec:
replicas: 3 # 生产环境多副本
selector:
matchLabels:
app: ai-observability
template:
metadata:
labels:
app: ai-observability
spec:
containers:
- name: app
image: spring-ai-observability:1.0.0
ports:
- containerPort: 8080
name: http
envFrom:
- configMapRef:
name: ai-observability-config
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
---
# ========== service.yaml ==========
apiVersion: v1
kind: Service
metadata:
name: ai-observability
spec:
selector:
app: ai-observability
ports:
- port: 80
targetPort: 8080
type: ClusterIP
部署命令:
bash
kubectl apply -f configmap.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl get pods -w # 观察启动状态
八、常见问题与排查指南
问题 1:Zipkin 控制台一片空白,看不到任何链路
排查流程图:
❌ 否
修复后
✅ 是
❌ 否
修复后
✅ 是
❌ 否
修复后
✅ 是
❌ 否
修复后
✅ 是
🎉 问题定位
⚠️ 仍未解决
🚨 问题
Zipkin 看不到数据
Zipkin 容器
运行正常?
🔧 诊断命令
docker ps | grep zipkin
docker logs zipkin
应用配置
正确?
⚙️ 配置检查
application.yml
zipkin.tracing.endpoint
网络
连通?
🌐 网络测试
curl 测试 Zipkin 地址
检查防火墙/安全组
采样率
设置正确?
🎯 调整采样率
tracing.sampling.probability: 1.0
📋 日志排查
grep -i 'zipkin\|brave\|trace'
查看日志
定位具体错误
📞 升级处理
查看社区/提交Issue
常见原因:
- 配置错误 :
zipkin.tracing.endpoint写成了http://localhost:9411,但应用跑在 Docker 里,应该用http://host.docker.internal:9411 - 采样率为 0 :
tracing.sampling.probability: 0.0表示不采集任何数据 - 异步延迟:Zipkin 默认批量发送,可能有 1-5 秒延迟,稍等再刷新
问题 2:应用启动报错 AI_DASHSCOPE_API_KEY not found
bash
# 快速诊断
echo $AI_DASHSCOPE_API_KEY # 如果为空,说明环境变量没生效
# 解决方案 1:命令行启动时传入
java -jar app.jar --ai.dashscope.api-key=sk-xxxxx
# 解决方案 2:写入 application-local.yml(开发环境)
# 不要提交到 Git!添加到 .gitignore
问题 3:图片接口返回 500
可能原因链:
- DashScope 生图成功,但返回的临时 URL 过期(默认 30 分钟)
- 服务器无法访问外网,下载图片失败
- 内存不足,大图片无法缓冲
排查命令:
bash
# 测试服务器能否访问 DashScope
curl -I https://dashscope.aliyuncs.com
# 查看具体错误日志
tail -f logs/application.log | grep -A 5 "Image generation failed"
问题 4:AI 响应特别慢,怎么定位瓶颈?
使用 Zipkin 精准定位:
- 打开 Zipkin,找到那条慢请求
- 看哪个 Span 耗时最长:
- 如果是
HTTP POST长 → 网络问题或 DashScope 服务端慢 - 如果是
AI Generation长 → 模型本身慢,考虑换轻量级模型 - 如果是
Stream Response长 → 前端接收慢或带宽不足
- 如果是
问题 5:采样率怎么配?
| 环境 | 采样率 | 理由 |
|---|---|---|
| 开发 | 1.0 |
100% 采样,方便调试 |
| 测试 | 0.5 |
50% 采样,平衡完整性和性能 |
| 生产 | 0.01 - 0.1 |
1%-10% 采样,QPS 高时避免性能开销 |
| 故障排查 | 1.0 |
临时调高,排查完再调回去 |
九、方案对比与选型决策
9.1 决策树
是
是
否
否
是
否
是
否
需要 AI 应用可观测性?
预算充足?
需要商业级
告警/拓扑图?
阿里云 ARMS
商业方案
本方案
Zipkin + Micrometer
需要长期存储?
Zipkin + Elasticsearch
持久化存储
Zipkin 内存模式
快速启动
需要指标告警?
Prometheus + Grafana
补充指标监控
Zipkin 自带查询
够用
9.2 功能对比矩阵
| 能力 | ARMS | Zipkin | Prometheus+Grafana | Jaeger |
|---|---|---|---|---|
| 链路追踪 | ✅ 完整 | ✅ 核心 | ❌ 无 | ✅ 完整 |
| 性能指标 | ✅ 丰富 | ⚠️ 基础 | ✅ 专业 | ⚠️ 基础 |
| 拓扑图 | ✅ 自动 | ❌ 无 | ❌ 无 | ✅ 自动 |
| 告警通知 | ✅ 内置 | ❌ 需自建 | ✅ AlertManager | ❌ 需自建 |
| 成本 | 💰 按量 | 🆓 免费 | 🆓 免费 | 🆓 免费 |
| 部署复杂度 | 低 | 中 | 中 | 中 |
十、进阶扩展
10.1 接入 Prometheus + Grafana(指标可视化)
xml
<!-- 添加依赖 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
配置:
yaml
management:
endpoints:
web:
exposure:
include: "prometheus,health,info" # 暴露 Prometheus 端点
效果:访问 http://localhost:8080/actuator/prometheus 可看到:
# HELP http_server_requests_seconds
# TYPE http_server_requests_seconds_count
http_server_requests_seconds_count{uri="/observability/chat",method="GET",status="200"} 42.0
10.2 自定义追踪埋点
java
@Service
@Slf4j
public class CustomService {
// 注入 Brave 的 Tracer
private final Tracer tracer;
public CustomService(Tracer tracer) {
this.tracer = tracer;
}
public String doSomething() {
// 手动创建一个 Span
Span span = tracer.nextSpan()
.name("custom-operation")
.tag("operation.type", "data-processing")
.tag("input.size", "1024")
.start();
try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
// 你的业务逻辑
Thread.sleep(100);
return "done";
} catch (Exception e) {
span.error(e); // 记录异常
throw new RuntimeException(e);
} finally {
span.end(); // 必须 end,否则链路不完整
}
}
}
10.3 替换为 OpenTelemetry(未来趋势)
OpenTelemetry 是 CNCF 主导的观测标准,正在统一 Metrics、Logs、Traces:
xml
<!-- 替换 Brave 为 OTel -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
<version>1.3.4</version>
</dependency>
<!-- OTel 的 Zipkin 导出器 -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-zipkin</artifactId>
</dependency>
优势:一份代码,同时对接 Zipkin、Jaeger、AWS X-Ray、Google Cloud Trace 等多种后端。
十一、一键启动脚本
bash
#!/bin/bash
set -e
# ========== 配置区(修改为你的真实值)==========
PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)"
export AI_DASHSCOPE_API_KEY="${AI_DASHSCOPE_API_KEY:-sk-你的通义千问Key}"
export WEATHER_API_KEY="${WEATHER_API_KEY:-你的天气Key}"
ZIPKIN_PORT=9411
APP_PORT=8080
# ========== 颜色输出 ==========
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# ========== 检查环境 ==========
log_info "检查环境..."
command -v java >/dev/null 2>&1 || { log_error "Java 未安装"; exit 1; }
command -v mvn >/dev/null 2>&1 || { log_error "Maven 未安装"; exit 1; }
command -v docker >/dev/null 2>&1 || { log_error "Docker 未安装"; exit 1; }
# ========== 启动 Zipkin ==========
log_info "启动 Zipkin..."
if docker ps | grep -q zipkin; then
log_warn "Zipkin 已在运行"
else
docker run -d --name zipkin -p ${ZIPKIN_PORT}:${ZIPKIN_PORT} --restart unless-stopped openzipkin/zipkin:latest >/dev/null 2>&1 || {
docker start zipkin >/dev/null 2>&1
}
sleep 3
fi
# 等待 Zipkin 就绪
for i in {1..10}; do
if curl -s http://localhost:${ZIPKIN_PORT} >/dev/null; then
log_info "Zipkin 已就绪: http://localhost:${ZIPKIN_PORT}"
break
fi
sleep 1
done
# ========== 构建项目 ==========
log_info "构建项目..."
cd "${PROJECT_DIR}"
mvn clean package -DskipTests -q
# ========== 启动应用 ==========
log_info "启动 Spring Boot 应用..."
log_info "API Key: ${AI_DASHSCOPE_API_KEY:0:10}..."
java -jar target/observability-example-1.0.0.jar &
APP_PID=$!
# 等待应用启动
for i in {1..30}; do
if curl -s http://localhost:${APP_PORT}/actuator/health >/dev/null 2>&1; then
log_info "应用已就绪: http://localhost:${APP_PORT}"
break
fi
sleep 1
done
# ========== 测试接口 ==========
log_info "测试接口..."
echo ""
echo "1. 对话接口: curl -N "http://localhost:${APP_PORT}/observability/chat?prompt=hello""
echo "2. 图片接口: curl "http://localhost:${APP_PORT}/observability/image/generate" -o test.png"
echo "3. 向量化: curl "http://localhost:${APP_PORT}/observability/embedding/simple""
echo "4. 天气工具: curl -N "http://localhost:${APP_PORT}/observability/tools?prompt=北京天气""
echo ""
echo "查看链路: http://localhost:${ZIPKIN_PORT}"
echo ""
# 保持运行
wait $APP_PID
保存为 start.sh,运行:
bash
chmod +x start.sh
./start.sh
十二、验证清单
部署完成后,逐项打勾确认:
-
docker ps看到zipkin容器在运行 - 访问 http://localhost:9411 能看到 Zipkin 界面
-
mvn clean package构建成功,无报错 - 启动日志出现
Started ObservabilityApplication -
curl http://localhost:8080/actuator/health返回{"status":"UP"} - 对话接口返回流式响应(逐字出现)
- 向量化接口返回
vectorSize数值 - Zipkin 中能看到至少一条 Trace 记录
- 点击 Trace 能看到多个 Span 组成的调用链
- 工具调用接口能返回天气信息(如果配置了天气 Key)
十三、总结与展望
本方案核心价值
| 优势 | 说明 |
|---|---|
| 零成本 | 不需要阿里云账号,不需要付费,Docker 一键启动 |
| 开箱即用 | 引入依赖即可,自动埋点,无需改业务代码 |
| 全链路覆盖 | Chat、Image、Embedding、ToolCalling 全部自动追踪 |
| 可扩展 | 轻松切换到 Prometheus、Jaeger、OpenTelemetry |
适合谁用?
- ✅ 初创团队,预算有限但需要可观测性
- ✅ 个人开发者,学习 Spring AI 和分布式追踪
- ✅ 已有 K8s 集群,想自建观测体系
- ✅ 对数据主权有要求,不想把数据放到公有云
下一步可以做什么?
- 接入 Prometheus :添加
micrometer-registry-prometheus,用 Grafana 做仪表盘 - 接入告警:用 AlertManager 配置"AI 接口 P99 延迟 > 5s 告警"
- 日志关联:在日志中打印 TraceID,实现日志-链路联动排查
- 性能调优:根据 Zipkin 数据,优化慢接口(加缓存、换模型、异步化)
- 多环境管理:用 Spring Profile 区分 dev/test/prod 的采样率配置
最后:可观测性不是"锦上添花",而是 AI 应用上线前的"必修课"。当用户投诉"AI 好慢"时,你能打开 Zipkin 精准定位到是网络延迟还是模型生成慢------这种"一切尽在掌握"的感觉,就是可观测性带来的底气。