【第27篇】Micrometer + Zipkin

从零开始,手把手教你用 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(必须):

  1. 访问 DashScope 控制台
  2. 点击「开通服务」→ 选择「通义千问」
  3. 创建 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 链路

  1. 打开浏览器访问 http://localhost:9411

  2. 点击「查找」按钮(不填条件查全部)

  3. 你应该看到类似这样的链路:

    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

常见原因

  1. 配置错误zipkin.tracing.endpoint 写成了 http://localhost:9411,但应用跑在 Docker 里,应该用 http://host.docker.internal:9411
  2. 采样率为 0tracing.sampling.probability: 0.0 表示不采集任何数据
  3. 异步延迟: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

可能原因链

  1. DashScope 生图成功,但返回的临时 URL 过期(默认 30 分钟)
  2. 服务器无法访问外网,下载图片失败
  3. 内存不足,大图片无法缓冲

排查命令

bash 复制代码
# 测试服务器能否访问 DashScope
curl -I https://dashscope.aliyuncs.com

# 查看具体错误日志
tail -f logs/application.log | grep -A 5 "Image generation failed"

问题 4:AI 响应特别慢,怎么定位瓶颈?

使用 Zipkin 精准定位

  1. 打开 Zipkin,找到那条慢请求
  2. 看哪个 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 集群,想自建观测体系
  • ✅ 对数据主权有要求,不想把数据放到公有云

下一步可以做什么?

  1. 接入 Prometheus :添加 micrometer-registry-prometheus,用 Grafana 做仪表盘
  2. 接入告警:用 AlertManager 配置"AI 接口 P99 延迟 > 5s 告警"
  3. 日志关联:在日志中打印 TraceID,实现日志-链路联动排查
  4. 性能调优:根据 Zipkin 数据,优化慢接口(加缓存、换模型、异步化)
  5. 多环境管理:用 Spring Profile 区分 dev/test/prod 的采样率配置

最后:可观测性不是"锦上添花",而是 AI 应用上线前的"必修课"。当用户投诉"AI 好慢"时,你能打开 Zipkin 精准定位到是网络延迟还是模型生成慢------这种"一切尽在掌握"的感觉,就是可观测性带来的底气。

相关推荐
DeepReinforce2 小时前
四、AI量化投资:使用akshare获取A股主板20260430筛选后的涨停股票
人工智能
qcx232 小时前
【AI Agent通识九课】02 · Agent 的“思考回路“长啥样?
人工智能·ai·llm·agent
翔云1234562 小时前
端侧推理:全面解析与深度洞察
人工智能·ai·大模型
AI精钢2 小时前
AI Agent 从上线到删库跑路始末
网络·人工智能·云原生·aigc
码农小旋风2 小时前
2026 终端 AI 编程工具深度横评:Claude Code、Codex CLI、Gemini CLI、Aider 怎么选
人工智能·gpt·claude
Chef_Chen3 小时前
论文解读:多模态智能体长期记忆突破:M3-Agent让AI像人一样“看、听、记、想“
人工智能·机器学习·agent·memory
zhuiyisuifeng3 小时前
2026AI办公革命:Gemini3.1Pro重塑职场效率
人工智能
threelab3 小时前
Three.js UV 图像变换效果 | 三维可视化 / AI 提示词
javascript·人工智能·uv
海兰3 小时前
【第28篇】可观测性实战:LangFuse 方案详解
人工智能·spring boot·alibaba·spring ai