Java 程序员第 46 阶段04:大模型调用链路追踪,SkyWalking 排查线上性能,Spring Boot接入实战

📅 发布时间:2026/8/13 11:49:10
Java 程序员第 46 阶段04:大模型调用链路追踪,SkyWalking 排查线上性能,Spring Boot接入实战 环境准备自动埋点接入Agent 方式application.yml 配置手动埋点Trace / ActiveSpan大模型调用链路追踪实战常见问题排查1. 环境准备本篇在 Spring Boot 3.x 工程上演示从零接入 SkyWalking并针对大模型OpenAI 风格 / 自研 LLM 网关调用做链路追踪。1.1 版本匹配组件版本建议说明---------Spring Boot3.2.xJDK 17SkyWalking9.7Agent 与 OAP 同版本OAP UI9.7独立部署StorageElasticsearch 8.x或 BanyanDB1.2 目录规划project/├── docker-compose.yml # OAP ES UI├── agent/│ └── skywalking-agent.jar # 探针└── src/main/resources/└── application.yml2. 自动埋点接入Agent 方式自动埋点无需改业务代码仅需在启动时挂载 Agent。SkyWalking 已内置对 Spring MVC、OpenFeign、RestTemplate、JDBC、Redis、Kafka 等组件的插件。2.1 启动脚本挂载 Agentjava -javaagent:/opt/agent/skywalking-agent.jar \-Dskywalking.agent.service_namechat-service \-Dskywalking.collector.backend_serviceoap:11800 \-jar chat-service.jar2.2 通过 IDEA / Maven 配置在 pom.xml 中约定 agent 路径也可在启动参数里直接写plugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactIdconfigurationjvmArguments-javaagent:${project.basedir}/agent/skywalking-agent.jar-Dskywalking.agent.service_namechat-service-Dskywalking.collector.backend_serviceoap:11800/jvmArguments/configuration/plugin启动后访问一次接口去 SkyWalking UI 的「拓扑」即可看到 chat-service 节点与其下游依赖。3. application.yml 配置除启动参数外部分行为建议落到配置文件或 agent.config中统一管理。# application.yml业务侧仅声明自身配置spring:application:name: chat-servicedatasource:url: jdbc:mysql://mysql:3306/chatredis:host: redis# SkyWalking 推荐通过 agent.config 控制关键项示例# agent.service_name${SW_AGENT_NAME:chat-service}# collector.backend_service${SW_BACKEND:oap:11800}# agent.sample_rate10000# plugin.toolkit.log.grpc.reporter.server_hostoap 最佳实践启动参数用于环境相关的地址/服务名agent.config 用于策略相关的采样率/忽略后缀二者配合避免硬编码。4. 手动埋点Trace / ActiveSpan自动埋点覆盖通用框架但**大模型调用、内部业务片段**需要用手动埋点补充否则链路会断或粗。4.1 Trace 注解SkyWalking 提供 org.apache.skywalking.apm.toolkit.trace.Trace 注解标注的方法会被当作一个 Local Span。import org.apache.skywalking.apm.toolkit.trace.Trace;import org.apache.skywalking.apm.toolkit.trace.Tag;Servicepublic class ChatService {Trace(operationName buildPrompt)Tag(key prompt.length, value arg[0].length())public String buildPrompt(String userQuery) {// 组装提示词作为独立 Local Span 便于定位耗时return PromptTemplate.render(userQuery);}}4.2 ActiveSpan 手动打 Tag / Log在方法内部可获取当前 Span附加自定义标签如大模型参数、Token 数import org.apache.skywalking.apm.toolkit.trace.ActiveSpan;public CompletionResult callLLM(String model, String prompt) {ActiveSpan.tag(llm.model, model);ActiveSpan.tag(llm.prompt.length, String.valueOf(prompt.length()));long start System.currentTimeMillis();CompletionResult result llmClient.complete(model, prompt);ActiveSpan.tag(llm.token.prompt, String.valueOf(result.promptTokens()));ActiveSpan.tag(llm.token.completion, String.valueOf(result.completionTokens()));ActiveSpan.tag(llm.cost.usd, result.costUsd().toString());ActiveSpan.tag(llm.ttft.ms, String.valueOf(result.timeToFirstTokenMs()));return result;}4.3 跨线程上下文传播异步调用会丢失上下文需用 SkyWalking 提供的包装器import org.apache.skywalking.apm.toolkit.trace.RunnableWrapper;CompletableFuture.supplyAsync(RunnableWrapper.of(() - {// 此处的 Span 上下文与父线程一致return callLLM(model, prompt);}));5. 大模型调用链路追踪实战下面用一个完整示例把「HTTP 调用 LLM 网关」纳入链路并采集 Token / 时延 Tag。5.1 自定义 LLM Exit Span 拦截器插件式增强对于 SkyWalking 未内置的 LLM SDK建议写一个自动插件参考第 03 篇在 SDK 的 complete 方法上织入 Exit Span// 简化手动方式在业务层包一层 ExitSpanpublic CompletionResult tracedComplete(String model, String prompt) {// 标记为 Exit跨进程访问远端 LLMAbstractSpan span ContextManager.createExitSpan(LLM/ model, new ContextCarrier(), llm-gateway:443);try {ActiveSpan.tag(llm.model, model);CompletionResult r llmClient.complete(model, prompt);ActiveSpan.tag(llm.token.prompt, String.valueOf(r.promptTokens()));ActiveSpan.tag(llm.ttft.ms, String.valueOf(r.timeToFirstTokenMs()));return r;} catch (Exception e) {ActiveSpan.error(e); // 标记 Span 异常throw e;} finally {ContextManager.stopSpan();}}5.2 Controller 串联整条链路RestControllerRequestMapping(/chat)public class ChatController {GetMapping(/ask)public MapString, Object ask(RequestParam String q) {// EntrySpan 由 Spring MVC 插件自动创建String prompt chatService.buildPrompt(q); // Trace Local SpanListDoc docs vectorService.search(q); // 自动 Exit Span(MySQL/向量)CompletionResult r llmGateway.tracedComplete(gpt-4o, prompt); // 手动 Exit SpanMapString, Object resp new HashMap();resp.put(answer, r.text());resp.put(tokens, r.promptTokens() r.completionTokens());return resp;}}此时一次 /chat/ask 调用在 UI 中呈现的 Span 树见图 figure_04_3Entry → buildPrompt(Local) → vectorSearch(Exit) → LLM/Exit各段时延与 Tag 一目了然。6. 常见问题排查6.1 拓扑看不到服务现象可能原因处理---------服务不显示Agent 未挂载 / backend_service 错误检查 -javaagent 与地址无 Trace采样率过低调高 agent.sample_rate链路断点跨进程未传播确认网关/SDK 透传 sw8 头6.2 链路断在异步/线程池现象下游服务的 Span 不在同一 Trace。原因多为线程切换丢失上下文。// 错误直接 new Thread上下文丢失new Thread(() - callLLM(...)).start();// 正确使用 RunnableWrapper 传播上下文new Thread(RunnableWrapper.of(() - callLLM(...))).start();6.3 大模型链路被采样丢弃大模型调用慢且贵必须排除采样。在 agent.configagent.sample_rate10000# 对慢端点强制记录OAP 侧 slowTraceThreshold 配置6.4 Agent 启动报错 ClassCircularityError多为插件冲突精简 plugins/ 目录只保留实际使用的插件如只留 spring-mvc、httpclient、jdbc、redis。小结本篇落地了 Spring Boot 接入 SkyWalking 的完整路径Agent 自动埋点零侵入接入通用组件Trace 与 ActiveSpan 补充业务与大模型段埋点通过 RunnableWrapper 解决异步上下文传播并给出链路断点、采样、插件冲突等常见坑的排查表。下一篇我们将系统规范大模型链路的 Trace/Span 建模与 Tag 命名让追踪数据真正可分析。