Article
第一章:SkyWalking 概述与核心概念
1.1 什么是 APM 与分布式追踪
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| APM (Application Performance Management) | 应用性能管理,用于监控、分析和优化应用程序的性能和可用性。 | APM 不仅关注性能,也包含错误追踪、用户体验、业务指标等。 |
| 分布式追踪 (Distributed Tracing) | 在微服务或分布式系统中,追踪一个请求从入口到各服务的完整调用链路的技术。 | 需要统一的追踪上下文传递机制(如 Trace Context)以保证跨服务链路的连续性。 |
| 调用链 (Trace) | 表示一个完整请求的执行路径,由多个 Span 组成。 | 一个 Trace 通常对应一个用户请求或一个业务事务。 |
| 上下文传播 (Context Propagation) | 在服务调用间传递追踪信息(如 Trace ID、Span ID)的机制。 | 常见传播格式:W3C Trace Context、B3 Headers。 |
| 性能瓶颈定位 | 通过分析调用链的耗时分布,快速定位慢请求发生在哪个服务或方法。 | 需结合日志、指标、追踪三位一体进行根因分析。 |
1.2 SkyWalking 架构概览(OAP Server, UI, Agent)
| 组件名称 | 说明 | 注意事项 |
|---|---|---|
| Agent (探针) | 嵌入在应用中的 Java Agent,负责自动或手动收集追踪、指标等数据。 | 零侵入或低侵入,通过字节码增强技术实现,对应用性能影响较小。 |
| OAP Server (Observability Analysis Platform) | 接收 Agent 上报的数据,进行分析、聚合、存储,并提供查询接口。 | 核心处理引擎,支持多种存储后端(如 Elasticsearch、MySQL、TiKV)。 |
| UI (User Interface) | 提供可视化界面,用于查看拓扑图、调用链、性能指标、告警等信息。 | 基于 Web 的前端,依赖 OAP Server 提供的 REST API 获取数据。 |
| 数据采集协议 | Agent 与 OAP Server 之间通过 gRPC 或 HTTP 协议传输数据。 | gRPC 性能更高,推荐生产环境使用;HTTP 便于调试。 |
| 插件机制 | Agent 支持多种中间件和框架的自动探针插件(如 Spring, Dubbo, MySQL)。 | 插件可扩展,社区活跃,支持主流技术栈。 |
1.3 核心概念解析:Trace、Segment、Span、Service、Endpoint
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Trace | 一个完整请求的全局追踪标识,包含该请求在所有服务中的执行路径。 | 所有相关 Span 共享同一个 Trace ID,用于跨服务关联。 |
| Segment | 一个服务实例内,一次请求执行的代码段快照,由多个 Span 组成。 | Segment 是 SkyWalking 的数据上报单位,每个 Segment 包含一个或多个 Span。 |
| Span | 表示一次操作的基本单元,如方法调用、数据库查询、HTTP 请求等。 | Span 有类型(Entry、Exit、Local),用于构建调用关系。 |
| Service | 逻辑上的服务单元,通常对应一个微服务应用。 | 多个实例(Instance)构成一个 Service。 |
| Endpoint | 服务中的具体接口或方法,如 /user/get 或 UserService.findById。 | 用于精细化监控接口级别的性能和错误率。 |
| Trace ID | 全局唯一的字符串,标识一个完整的调用链。 | 通常由第一个 Entry Span 生成,后续 Span 继承并传播。 |
| Span ID | 在单个 Segment 内唯一,标识一个操作。 | 与 Parent Span ID 构成树形结构。 |
| Parent Span ID | 当前 Span 的父 Span 的 ID,用于构建调用层级。 | Root Span 的 Parent Span ID 为空。 |
第二章:环境搭建与快速入门
2.1 下载与安装 SkyWalking OAP Server
| 操作步骤 | 说明 | 注意事项 |
|---|---|---|
| 访问官方发布页 | 前往 SkyWalking GitHub Releases 页面下载最新稳定版本。 | 推荐使用最新稳定版(如 9.x),避免使用快照版本用于生产。 |
| 选择分发包 | 下载 apache-skywalking-apm-x.x.x.tar.gz 或 .zip 文件。 | 包含 OAP Server 和 UI,无需单独下载。 |
| 解压安装包 | 使用 tar 或 unzip 解压到目标目录。 | 确保路径无中文和空格,避免启动问题。 |
| 目录结构说明 | bin/:启动脚本;config/:配置文件;webapp/:UI 静态资源。 | config/application.yml 是 OAP Server 主配置文件。 |
| 存储后端配置 | 默认使用 H2 内存数据库,生产环境需修改为 Elasticsearch 等持久化存储。 | 需提前部署并配置好 Elasticsearch 集群。 |
2.2 启动 SkyWalking UI
| 操作步骤 | 说明 | 注意事项 |
|---|---|---|
| 配置 UI 端口 | 修改 webapp/webapp.yml 中的 server.port(默认 8080)。 | 若端口冲突,需修改为可用端口(如 8081)。 |
| 配置 OAP 地址 | 确保 webapp/webapp.yml 中 collector.ribbon.listOfServers 指向 OAP Server 地址。 | 默认为 localhost:12800,若 OAP 部署在远程机器,需修改为对应 IP。 |
| 启动 OAP Server | 执行 bin/oapService.sh(Linux)或 bin/oapService.bat(Windows)。 | 需先启动 OAP Server,再启动 UI。 |
| 启动 UI 服务 | 执行 bin/webappService.sh 或 bin/webappService.bat。 | UI 启动较慢,等待日志出现 “Started Jetty in” 表示成功。 |
| 访问 UI 界面 | 浏览器访问 http://<host>:<port>(如 http://localhost:8080)。 | 首次访问可能无数据,需接入应用并产生请求。 |
2.3 使用 Java Agent 快速接入应用
| 操作步骤 | 说明 | 注意事项 |
|---|---|---|
| 找到 Agent 目录 | 解压包中的 agent/ 目录包含探针文件。 | 确保 Agent 目录路径无中文和空格。 |
| 启动参数配置 | 在 Java 应用启动命令中添加 -javaagent:/path/to/skywalking-agent.jar。 | Agent JAR 路径必须正确,建议使用绝对路径。 |
| 设置服务名 | 添加 -Dskywalking.agent.service_name=my-service。 | 服务名应具有业务意义,避免重复。 |
| 设置 OAP 地址 | 添加 -Dskywalking.collector.backend_service=localhost:11800。 | 端口 11800 是 gRPC 上报端口,确保 OAP Server 已监听。 |
| 完整启动命令示例 | java -javaagent:/opt/skywalking/agent/skywalking-agent.jar -Dskywalking.agent.service_name=demo-service -Dskywalking.collector.backend_service=192.168.1.100:11800 -jar myapp.jar | 可将参数写入脚本,便于管理。 |
| 容器化部署 | 在 Dockerfile 或 Kubernetes 中挂载 Agent 目录并配置启动参数。 | 注意容器内路径映射和权限问题。 |
2.4 验证数据上报与 UI 查看基础指标
| 操作步骤 | 说明 | 注意事项 |
|---|---|---|
| 触发应用请求 | 调用已接入 Agent 的应用接口,产生业务流量。 | 至少发起一次成功请求,确保数据生成。 |
| 查看服务拓扑图 | UI 首页”拓扑图”页面,查看服务间调用关系。 | 新服务可能需要等待 1-2 分钟才会显示。 |
| 查看调用链 (Trace) | 进入”追踪”页面,筛选服务名,查看最近的 Trace 列表。 | 点击 Trace 可查看详细 Span 信息和耗时。 |
| 查看 Metrics | 在”仪表板”中查看 QPS、响应时间、错误率等基础指标。 | 指标为近实时,可能存在 10-30 秒延迟。 |
| 检查 Agent 日志 | 查看应用日志或 agent/logs/skywalking-api.log 是否有错误。 | 常见问题:网络不通、OAP 地址错误、端口未开放。 |
| 验证数据完整性 | 确认 Trace 中包含预期的 Span(如 HTTP、DB 调用)。 | 若缺少 Span,检查插件是否支持对应组件。 |
第三章:Java Agent 自动探针原理与配置
3.1 Java Agent 工作机制(Instrumentation)
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Java Agent | JVM 提供的一种在类加载时动态修改字节码的机制,实现无侵入监控。 | 需通过 -javaagent 参数启动,优先于应用代码加载。 |
| Instrumentation API | JDK 提供的 java.lang.instrument 包,用于实现字节码增强。 | 核心接口:Instrumentation,通过 premain 或 agentmain 方法注入。 |
| 字节码增强 (Bytecode Enhancement) | 在类加载时修改其字节码,插入监控逻辑(如方法前后插入计时代码)。 | 使用 ASM、Javassist 等库操作字节码,性能开销小。 |
| ClassFileTransformer | Instrumentation 接口注册的类文件转换器,对指定类进行字节码修改。 | 可匹配类名、方法名、注解等条件进行精准增强。 |
| Agent-Class | Agent 的入口类,包含 premain 方法,用于初始化探针逻辑。 | premain 方法在应用 main 方法前执行。 |
| 插件化架构 | SkyWalking Agent 使用插件机制,每个中间件(如 MySQL, Redis)有独立插件。 | 插件定义匹配规则和增强逻辑,易于扩展和维护。 |
| 方法拦截 (Method Interceptor) | 在目标方法执行前后插入自定义逻辑(如记录开始/结束时间)。 | 拦截器需处理异常,避免影响原方法执行。 |
3.2 Agent 核心配置文件详解(agent.config)
| 配置项名称 | 说明 | 注意事项 |
|---|---|---|
agent.service_name | 应用服务名称,显示在 UI 中。 | 必须配置,建议使用小写字母和连字符,如 order-service。 |
agent.sample_n_per_3_secs | 每 3 秒采样 Span 数量,默认 -1(全量)。 | 生产环境建议设置合理采样率(如 10)以降低开销。 |
collector.backend_service | OAP Server 的 gRPC 地址,格式为 <host>:<port>。 | 端口默认 11800,确保网络可达。 |
agent.namespace | 服务命名空间,用于多租户隔离。 | 默认为空,按需配置。 |
agent.ignore_suffix | 配置忽略追踪的 URL 后缀,如 .css,.js,.png。 | 多个后缀用英文逗号分隔,减少静态资源干扰。 |
agent.cause_exception_depth | 记录异常堆栈的深度,默认 5。 | 值越大记录越详细,但增加存储开销。 |
logging.level | Agent 自身日志级别,如 DEBUG, INFO, WARN, ERROR。 | 生产环境建议使用 INFO 或 WARN。 |
plugin.springmvc.use_short_url | Spring MVC 接口是否使用短 URL(去除路径参数)。 | 设为 true 可聚合相同模式的接口(如 /user/1 → /user/{id})。 |
plugin.dubbo.collect_arguments | 是否收集 Dubbo 方法参数。 | 参数可能包含敏感信息,按需开启。 |
profiling.active_controller_backends | 启用性能剖析功能的后端地址。 | 高级功能,需配合 OAP 配置使用。 |
3.3 支持的中间件与框架自动探测
| 中间件/框架 | 支持的功能 | 注意事项 |
|---|---|---|
| Spring MVC / WebFlux | 自动探测 HTTP 接口,生成 Entry Span。 | 支持路径参数聚合,需配置 plugin.springmvc.use_short_url。 |
| Dubbo / gRPC | 接口级服务调用追踪,生成 Exit Span 和远程服务 Span。 | 需确保服务提供方和消费方都接入 SkyWalking。 |
| MySQL / PostgreSQL | SQL 语句追踪,记录数据库操作、参数(可选)、执行时间。 | 敏感参数可通过配置脱敏或关闭参数收集。 |
| Redis (Jedis, Lettuce) | Redis 命令追踪,如 GET, SET, HGET 等。 | 不记录具体值,仅记录命令和 Key。 |
| Kafka / RabbitMQ | 消息生产与消费链路追踪,支持上下文传播。 | 消费者需正确配置,确保 Span 关联。 |
| Elasticsearch | REST/Transport Client 请求追踪。 | 记录查询 DSL 片段(可配置),注意隐私。 |
| OkHttp / HttpClient | HTTP 客户端调用追踪,生成 Exit Span。 | 支持异步调用链路传递。 |
| ZooKeeper | ZK 客户端操作追踪,如 create, get。 | 用于分析服务发现性能。 |
| ShardingSphere | 分库分表 SQL 与执行计划追踪。 | 需使用 SkyWalking 支持的版本。 |
| Log4j2 / Logback | 通过 MDC 自动注入 Trace ID,实现日志与追踪关联。 | 需启用日志插件并配置 MDC 输出 %X{trace_id}。 |
3.4 排除特定类或方法的追踪
| 配置方式 | 说明 | 注意事项 |
|---|---|---|
agent.ignore_suffix | 在 agent.config 中配置 URL 后缀,忽略静态资源或健康检查接口。 | 适用于 Web 接口,如 /health,/info,.css。 |
trace.ignore.path | 通过 JVM 参数设置忽略的 HTTP 路径,多个路径用英文逗号分隔。 | 优先级高于 agent.ignore_suffix,如 -Dtrace.ignore.path=/health,/metrics。 |
| 插件排除规则 | 在插件配置中禁用特定中间件的探测(如不监控 Redis)。 | 修改 agent/config/ 下对应插件的启用开关。 |
| 自定义 Ignore 字段 | 在代码中通过设置 Span Tag 忽略上报(需结合手动埋点)。 | 非标准做法,不推荐。 |
| 字节码匹配排除 | 通过修改插件源码或扩展插件,添加类/方法匹配排除规则。 | 高级用法,需重新打包 Agent。 |
| 环境变量配置 | 使用环境变量覆盖 agent.config 配置,便于容器化部署。 | 如 SW_AGENT_IGNORE_SUFFIX=.js,.css。 |
第四章:手动埋点与 OpenTracing API
4.1 OpenTracing 核心接口概述(Tracer, Span, Scope)
| 接口名称 | 说明 | 注意事项 |
|---|---|---|
| Tracer | 用于创建 Span 和管理追踪上下文的核心工厂接口。 | SkyWalking 通过 GlobalTracer.get() 获取实例。 |
| Span | 表示一个操作的基本单元,包含操作名、开始时间、标签、日志等信息。 | 必须显式结束(span.finish())以正确上报。 |
| SpanContext | Span 的上下文信息,包含 Trace ID、Span ID、Baggage 等。 | 用于跨服务传递追踪信息。 |
| Scope | 表示当前线程的活动 Span,通过 ScopeManager 管理。 | 使用 try-with-resources 确保自动关闭。 |
| ScopeManager | 管理线程本地的当前 Span(active span),处理跨线程传递。 | SkyWalking 使用 ThreadLocal 存储当前 Span。 |
| Baggage | 在 SpanContext 中传递的键值对数据,随整个 Trace 传播。 | 不用于监控,仅用于业务数据传递,避免滥用。 |
| Reference | 定义 Span 间的引用关系,如 CHILD_OF、FOLLOWS_FROM。 | SkyWalking 主要使用 CHILD_OF 构建树形调用链。 |
4.2 创建和结束 Span
| 方法名称 / 操作 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
Tracer.buildSpan | tracer.buildSpan("operationName") | 创建 Span 构建器,指定操作名称。 | Tracer tracer = GlobalTracer.get();Span span = tracer.buildSpan("userService.findById").start(); | 操作名应具有业务语义,便于在 UI 中识别。 |
AbstractSpan.setOperationName | span.setOperationName("newName") | 动态修改 Span 的操作名称。 | span.setOperationName("user-query"); | 通常在创建时指定,运行时修改较少。 |
Span.start | span.start() | 显式启动 Span,记录开始时间。 | span.start(); | buildSpan 后需调用 start() 才真正开始计时。 |
Span.finish | span.finish() | 结束 Span,记录结束时间并准备上报。 | span.finish(); | 必须调用,否则 Span 不会上报且可能内存泄漏。 |
| try-with-resources + Scope | try (Scope scope = tracer.buildSpan(...).startActive(true)) { ... } | 自动管理 Scope 生命周期,确保 Span 正确结束。 | try (Scope scope = tracer.buildSpan("businessLogic").startActive(true)) { // 业务代码} | 推荐方式,避免忘记 finish。 |
Span.log(Throwable) | span.log(event) | 记录异常事件到 Span 日志中。 | span.log(new RuntimeException("DB timeout")); | 可结合 Tag "error" = true 标记错误 Span。 |
4.3 添加 Span 标签(Tags)与日志(Logs)
| 方法名称 / 操作 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
Span.setTag(String key, String value) | span.setTag("key", "value") | 为 Span 添加字符串类型的标签。 | span.setTag("user.id", "12345");span.setTag("error", "true"); | 常用标签:error, db.type, http.method。 |
Span.setTag(String key, boolean value) | span.setTag("error", true) | 添加布尔类型标签,常用于标记错误。 | span.setTag("error", exception != null); | SkyWalking UI 会根据 error=true 高亮显示错误 Span。 |
Span.setTag(String key, Number value) | span.setTag("http.status_code", 200) | 添加数字类型标签,如状态码、数量。 | span.setTag("http.status_code", response.getStatus()); | 便于后续聚合分析。 |
Span.log(long timestamp, Map<String,?> fields) | 如右 | 在指定时间点记录结构化日志事件。 | Map<String, Object> log = new HashMap<>();log.put("event", "cache.miss");log.put("key", "user:1");span.log(System.currentTimeMillis(), log); | 可记录缓存命中、重试次数等事件。 |
Span.log(String event) | span.log("cache.hit"); | 记录简单的事件名称。 | span.log("db.query.start");span.log("db.query.end"); | 事件名应具有可读性。 |
| 内置标准标签常量 | Tags.HTTP_METHOD, Tags.HTTP_STATUS, Tags.DB_TYPE, Tags.ERROR | 使用 OpenTracing 规范的预定义标签,提高兼容性。 | span.setTag(Tags.HTTP_METHOD, "GET");span.setTag(Tags.ERROR, true); | 推荐使用标准标签,便于工具解析。 |
4.4 跨线程传递上下文(Scope Manager)
| 方法名称 / 操作 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
tracer.scopeManager().active() | Scope activeScope = tracer.scopeManager().active(); | 获取当前线程的活动 Scope。 | Scope parentScope = tracer.scopeManager().active(); | 若无活动 Span,返回 null。 |
Scope.span() | Span activeSpan = activeScope.span(); | 从 Scope 中获取当前活动 Span。 | Span parentSpan = parentScope.span(); | 用于跨线程传递 SpanContext。 |
SpanContext.inject | carrier.inject(activeSpan.context(), Format.Builtin.HTTP_HEADERS, httpHeaders); | 将 SpanContext 注入到载体(如 HTTP Headers)中。 | TextMapInjectAdapter carrier = new TextMapInjectAdapter(headers);activeSpan.context().inject(Format.Builtin.HTTP_HEADERS, carrier); | 使用 W3C 或 B3 格式进行跨服务传播。 |
SpanContext.extract | SpanContext extractedContext = tracer.extract(Format.Builtin.HTTP_HEADERS, carrier); | 从载体(如 HTTP Headers)中提取 SpanContext。 | TextMapExtractAdapter carrier = new TextMapExtractAdapter(headers);SpanContext ctx = tracer.extract(Format.Builtin.HTTP_HEADERS, carrier); | 消费方用于恢复调用链上下文。 |
try (Scope ignored = tracer.activateSpan(extractedContext)) { ... } | — | 在新线程中激活提取的 SpanContext,创建新的本地 Span。 | try (Scope scope = tracer.activateSpan(remoteContext)) { // 新线程中的代码} | 确保跨线程调用链连续。 |
| 异步任务包装 | 使用 CompletableFuture 时手动传递上下文。 | 保持异步调用链路完整。 | 如左 | SkyWalking 自动探针支持部分异步框架,手动埋点需特别注意。 |
4.5 设置 Span 层级关系(Parent-Child)
| 方法名称 / 操作 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
tracer.buildSpan(operationName).start() | 默认使用当前线程的活动 Span 作为父 Span。 | 创建子 Span,自动关联父 Span。 | try (Scope parent = tracer.buildSpan("parent").startActive(true)) { Span child = tracer.buildSpan("child").start(); child.finish();} | 最常用方式,依赖 ScopeManager。 |
tracer.buildSpan(operationName).asChildOf(Parent) | tracer.buildSpan("child").asChildOf(parentSpan).start() | 显式指定父 Span,构建父子关系。 | Span child = tracer.buildSpan("db.query").asChildOf(serviceSpan).start(); | 当无法使用 Scope 时使用。 |
tracer.buildSpan(operationName).withActiveSpan() | tracer.buildSpan("child").withActiveSpan().start() | 显式使用当前活动 Span 作为父 Span(与默认行为一致)。 | Span child = tracer.buildSpan("local.task").withActiveSpan().start(); | 代码更明确,可读性好。 |
tracer.buildSpan(operationName).ignoreActiveSpan() | tracer.buildSpan("independent").ignoreActiveSpan().start() | 忽略当前活动 Span,创建独立的 Span(无父 Span)。 | Span independent = tracer.buildSpan("background.job").ignoreActiveSpan().start(); | 用于完全独立的任务,不希望影响主调用链。 |
Reference.CHILD_OF | tracer.buildSpan("child").addReference(References.CHILD_OF, parentContext).start() | 使用 OpenTracing 标准引用方式建立父子关系。 | 如左 | 标准做法,但 asChildOf 更简洁。 |
| Root Span | 在无活动 Span 时创建的第一个 Span 成为 Root Span。 | 构成 Trace 的起点。 | Entry Span(如 HTTP 请求)通常是 Root Span。 | 一个 Trace 有且仅有一个 Root Span。 |
第五章:OpenTelemetry 与 SkyWalking 集成
5.1 OpenTelemetry SDK 基本使用
| 方法名称 / 操作 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
OpenTelemetry.getGlobalTracerProvider() | Tracer tracer = OpenTelemetry.getGlobalTracerProvider().get("my-library"); | 获取全局 Tracer 实例。 | Tracer tracer = OpenTelemetry.getGlobalTracerProvider().get("demo-app"); | 需先配置并设置全局 SDK,否则返回默认无操作实现。 |
Tracer.spanBuilder | Span span = tracer.spanBuilder("operation").startSpan(); | 创建 Span 构建器并启动 Span。 | Span span = tracer.spanBuilder("getData").startSpan(); | 必须调用 startSpan() 才真正创建 Span。 |
Span.setAttribute | span.setAttribute("key", "value") | 为 Span 添加属性(等同于 Tag)。 | span.setAttribute("user.id", "1001"); | 支持 String、long、double、boolean 及其数组类型。 |
Span.addEvent | span.addEvent("eventName") | 在 Span 中记录一个事件(等同于 Log)。 | span.addEvent("cache.miss"); | 可带属性:span.addEvent("retry", Attributes.of("count", 2)); |
Span.recordException | span.recordException(exception) | 记录异常信息,自动设置 error=true 属性。 | try { ... } catch (Exception e) { span.recordException(e); } | 推荐方式标记错误,自动填充异常消息和堆栈。 |
span.end() | span.end() | 结束 Span,释放资源并准备上报。 | span.end(); | 必须调用,否则造成内存泄漏。 |
try (Scope scope = span.makeCurrent()) | try (Scope scope = span.makeCurrent()) { ... } | 将 Span 设置为当前线程的活动 Span。 | try (Scope scope = span.makeCurrent()) { // 此范围内当前 Span 为 span} | 推荐使用 try-with-resources 确保自动清理。 |
SdkTracerProvider.builder() | SdkTracerProvider.builder().addSpanProcessor(...).build() | 构建自定义 Tracer Provider,用于配置处理器。 | 如 5.2 节示例 | 通常在应用初始化时配置一次。 |
5.2 使用 OTLP 上报数据到 SkyWalking
| 方法名称 / 操作 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
OtlpGrpcSpanExporter.builder() | OtlpGrpcSpanExporter.builder().setEndpoint("http://oap:11800").build() | 创建 OTLP gRPC Span 上报器,指向 SkyWalking OAP。 | OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter.builder() .setEndpoint("http://localhost:11800") .build(); | 端口 11800 是 SkyWalking OAP 的 OTLP gRPC 接收端口。 |
BatchSpanProcessor.builder() | BatchSpanProcessor.builder(exporter).build() | 创建批处理处理器,缓冲 Span 并批量上报。 | BatchSpanProcessor processor = BatchSpanProcessor.builder(exporter).build(); | 比 SimpleSpanProcessor 性能更好,减少网络开销。 |
SdkTracerProvider.builder() | SdkTracerProvider.builder().addSpanProcessor(processor).build() | 将处理器注册到 Tracer Provider。 | SdkTracerProvider provider = SdkTracerProvider.builder() .addSpanProcessor(processor) .build(); | 一个 Provider 可添加多个处理器。 |
OpenTelemetrySdk.builder() | OpenTelemetrySdk.builder().setTracerProvider(provider).build() | 构建完整的 OpenTelemetry SDK 实例。 | OpenTelemetrySdk sdk = OpenTelemetrySdk.builder() .setTracerProvider(provider) .build(); | 需设置为全局实例:OpenTelemetry.setGlobalTelemetry(sdk); |
Resource.create() | Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, "my-service")) | 创建资源对象,设置服务名等全局属性。 | Resource resource = Resource.create(Attributes.of( ResourceAttributes.SERVICE_NAME, "order-service")); | 必须设置 service.name,否则 SkyWalking 无法识别服务。 |
| 设置环境变量 | export OTEL_EXPORTER_OTLP_ENDPOINT=http://oap:11800 | 通过环境变量配置 OTLP 端点,无需代码配置。 | export OTEL_EXPORTER_OTLP_ENDPOINT=http://192.168.1.10:11800 | 适用于容器化部署,优先级低于代码配置。 |
5.3 映射 OpenTelemetry 数据到 SkyWalking 模型
| OpenTelemetry 概念 | SkyWalking 映射目标 | 说明 | 注意事项 |
|---|---|---|---|
| Trace ID | Trace ID | 全局唯一标识,直接透传。 | 格式兼容,无需转换。 |
| Span ID | Span ID | 操作唯一标识,直接透传。 | SkyWalking 使用相同 ID 空间。 |
| Parent Span ID | Parent Span ID | 构建调用树的父子关系。 | OTel 的 Parent ID 对应 SkyWalking 的 Parent Span ID。 |
| Span Kind (CLIENT, SERVER, INTERNAL, PRODUCER, CONSUMER) | Span Type (Exit, Entry, Local) | CLIENT/PRODUCER → Exit; SERVER/CONSUMER → Entry; INTERNAL → Local。 | SkyWalking 通过此映射构建拓扑图和服务依赖。 |
| Service Name (Resource) | Service Name | 逻辑服务名称,来自 Resource 属性。 | 必须设置 service.name 属性。 |
| Operation Name | Endpoint Name / Span Operation | 接口或操作名称,如 HTTP 路径、方法名。 | SkyWalking UI 按此名称聚合。 |
| Attributes (key-value) | Tags | 透传为 Span 的标签。 | 标准属性(如 http.method, db.statement)被 SkyWalking 语义化解析。 |
| Events | Logs | OTel 事件映射为 SkyWalking Span 日志。 | 包含时间戳和结构化字段。 |
| Status (OK, ERROR) | Error Tag | Status=ERROR 时设置 error=true 标签。 | SkyWalking 根据此标记错误请求。 |
| Instrumentation Library | 无直接对应 | 记录埋点库信息,SkyWalking 用作元数据。 | 不影响核心模型,用于调试和溯源。 |
第六章:高级特性与自定义监控
6.1 自定义指标(Metrics)上报
| 方法名称 / 操作 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
MeterProvider.get("name") | Meter meter = OpenTelemetry.getMeter("my-meter"); | 获取 Meter 实例,用于创建指标。 | Meter meter = OpenTelemetry.getMeter("jvm-monitor"); | 类似 Tracer,通过名称获取。 |
Meter.counterBuilder | Counter counter = meter.counterBuilder("requests.count").build(); | 创建计数器,单调递增。 | Counter reqCounter = meter.counterBuilder("http.requests").build(); | 适用于请求数、错误数等。 |
Counter.add | counter.add(1, "key", "value") | 增加计数器值,并可添加标签。 | reqCounter.add(1, "method", "GET", "path", "/api/user"); | 原子操作,线程安全。 |
Meter.upDownCounterBuilder | UpDownCounter upDownCounter = meter.upDownCounterBuilder("queue.size").build(); | 创建可增可减的计数器。 | 如左 | 适用于队列大小、连接数等。 |
UpDownCounter.add | upDownCounter.add(1);upDownCounter.add(-1); | 增加或减少计数器值。 | activeConnections.add(1);activeConnections.add(-1); | 值可为负。 |
Meter.histogramBuilder | Histogram histogram = meter.histogramBuilder("response.time").build(); | 创建直方图,用于统计分布(如响应时间)。 | Histogram respHist = meter.histogramBuilder("http.resp.time").build(); | SkyWalking 可计算 P90、P99 等。 |
Histogram.record | histogram.record(100.5, "path", "/api/user"); | 记录一个测量值。 | respHist.record(responseTime, "method", request.getMethod()); | 值通常为正数。 |
| LongSumObserver / DoubleSumObserver | 通过 Callback 注册异步指标采集。 | 采集不频繁变化的指标,如 JVM 内存。 | meter.sumObserverBuilder("jvm.memory.used") .setDescription("Used memory") .setUnit("bytes") .buildWithCallback(observer -> { observer.observe(getMemoryUsed(), "area", "heap"); }); | 异步上报,避免频繁调用。 |
6.2 添加全局标签(Global Tags)
| 配置方式 | 说明 | 注意事项 |
|---|---|---|
agent.config 配置 | 在 agent.config 中设置 agent.global_tags = tag1=value1,tag2=value2。 | 所有自动和手动创建的 Span 都会携带这些标签。 |
| JVM 系统属性 | 启动时添加 -Dskywalking.agent.global_tags=env=prod,region=us-east。 | 优先级高于配置文件,便于不同环境动态设置。 |
| OpenTelemetry Resource | 在 OTel 中通过 Resource 设置全局属性。 | Resource resource = Resource.create(Attributes.of("env", "prod", "version", "1.0")); |
| Agent 插件扩展 | 通过自定义插件在运行时动态添加标签(如从 MDC 读取)。 | 高级用法,需开发插件。 |
| 环境变量 | 设置 SW_AGENT_GLOBAL_TAGS=service.version=1.2.0。 | 与 JVM 参数效果相同,适用于容器环境。 |
| 注意事项 | 全局标签会增加每个 Span 的大小,避免设置过多或过大的值。 | 建议仅设置关键维度,如 env、version、region。 |
6.3 使用插件扩展探针能力
| 操作步骤 | 说明 | 注意事项 |
|---|---|---|
| 创建插件模块 | 新建 Maven 模块,依赖 skywalking-java-agent-plugin-define。 | 遵循 SkyWalking 插件命名规范。 |
| 定义 Instrumentation | 继承 ClassInstanceMethodsEnhancePluginDefine,指定目标类和方法。 | 可使用字节码匹配表达式(如 *Controller)精准定位。 |
| 实现 StaticMethodsAroundInterceptor | 编写拦截器,在方法前后插入逻辑。 | onMethodEnter() 中创建 Span,onMethodExit() 中结束 Span。 |
| 配置 plugin.def 文件 | 在 resources/META-INF/services/ 下创建文件,注册插件。 | 格式:插件类全名=增强类全名。 |
| 打包插件 JAR | 将插件 JAR 放入 agent/plugins/ 目录。 | Agent 启动时自动加载。 |
| 调试插件 | 启用 agent 日志 DEBUG 级别,观察插件加载和拦截日志。 | 使用 SkyWalking 提供的调试工具。 |
| 版本兼容性 | 插件需与 Agent 版本兼容,关注 API 变更。 | 建议使用稳定版本的 API。 |
6.4 日志与追踪上下文关联(Logging MDC)
| 配置方式 | 说明 | 注意事项 |
|---|---|---|
| 启用日志插件 | SkyWalking Agent 自带 log4j2/logback 插件,自动注入 MDC。 | 确保 agent.config 中插件已启用(默认开启)。 |
| MDC 键名 | trace_id, trace_id_readable, span_id, parent_span_id, service_name, endpoint_name | 在日志格式中使用这些键名输出上下文。 |
| 日志框架配置 | 在 logback.xml 或 log4j2.xml 中配置 PatternLayout 包含 MDC 字段。 | 例如:%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %X{trace_id} %msg%n |
| 手动设置 MDC(备用) | 在代码中使用 MDC.put("trace_id", span.getSpanContext().getTraceId()); | 仅在 Agent 未生效时使用,避免重复设置。 |
| 日志收集系统 | 将包含 Trace ID 的日志发送到 ELK 或其他日志系统。 | 在 Kibana 等工具中可通过 Trace ID 关联查看日志和调用链。 |
| 跨线程传递 | MDC 通常不跨线程,需使用特殊处理(如 TtlMDC)或依赖 Agent 自动处理。 | SkyWalking 插件会尽量保证异步场景下的上下文传递。 |
| 性能影响 | MDC 操作有轻微开销,但通常可忽略。 | 避免在高频日志中频繁操作 MDC。 |
第七章:性能分析与告警
7.1 查看调用链(Trace)与拓扑图
| 功能区域 | 操作说明 | 注意事项 |
|---|---|---|
| 服务拓扑图 (Topology) | 在 SkyWalking UI 首页查看全局服务依赖关系图,节点为服务,连线为调用关系。 | 颜色深浅表示调用量或延迟,红色表示高错误率。 |
| 服务列表 (Service List) | 查看所有已注册服务,可按名称、类型、SLA、响应时间等排序和筛选。 | 点击服务进入详情页,查看其调用链、指标等。 |
| 调用链查询 (Trace) | 在”Trace”页面,通过服务名、端点、时间范围、Trace ID 等条件查询调用链。 | 支持高级搜索,如 status:error 查看所有错误调用。 |
| 调用链详情 | 点击单条 Trace 查看完整的 Span 层级结构,展示调用顺序、耗时、标签和事件。 | 可展开每个 Span 查看详细属性,定位耗时瓶颈。 |
| 关联日志 | 在 Span 详情中查看注入的 Trace ID 和 Span ID,用于在日志系统中关联查询。 | 需确保日志框架已正确配置 MDC。 |
| 端点 (Endpoint) | 查看服务内具体接口(如 /api/user/{id})的性能指标和调用链。 | 可分析特定接口的 P99、QPS、错误率。 |
| 服务实例 (Instance) | 查看服务每个实例的健康状况和资源消耗(需开启 JVM 插件)。 | 用于排查单个实例异常。 |
| 浏览器端监控 (Browser) | 查看前端页面的性能数据(加载时间、AJAX 调用),与后端调用链关联。 | 需在前端页面引入 SkyWalking 的 JS Agent。 |
7.2 分析慢调用与错误率
| 分析方法 | 操作步骤 | 工具/功能 |
|---|---|---|
| 慢调用分析 | 1. 在 Trace 页面按响应时间排序。 2. 选择高延迟的 Trace。 3. 查看 Span 耗时分布,定位慢操作(如 DB、RPC)。 | 调用链详情图、Span 耗时柱状图。 |
| 错误率分析 | 1. 在服务或端点详情页查看 Error Rate 指标。 2. 在 Trace 搜索中使用 status:error。3. 分析错误 Span 的异常日志和堆栈。 | 错误率曲线图、错误调用链列表、异常堆栈信息。 |
| 指标对比 | 对比不同时间段、不同服务实例的响应时间、QPS、错误率,识别异常波动。 | SkyWalking UI 的指标对比功能(时间范围选择器)。 |
| 数据库慢查询 | 查看 DB 类型的 Exit Span,分析 SQL 语句和执行时间。 | 需确保 DB 插件已启用并能捕获 SQL。 |
| 缓存效率 | 分析 Redis/Memcached 调用的耗时和频率,判断缓存命中率。 | 通过 Exit Span 的操作类型(GET/SET)和耗时判断。 |
| 外部服务依赖 | 检查调用第三方 API 的 Exit Span,确认是否为瓶颈或错误源。 | 关注 HTTP 状态码、响应时间。 |
| JVM 性能 | 查看服务实例的 CPU、内存、GC 情况,判断是否资源不足导致性能下降。 | 需开启 JVM 插件,查看实例监控指标。 |
| 自定义指标分析 | 结合自定义 Metrics(如队列长度、锁等待时间)进行综合分析。 | 在 Metrics 页面查询自定义指标。 |
7.3 设置告警规则(Alarm Rules)
| 规则类型 | 配置示例 | 说明 |
|---|---|---|
| 服务级别规则 | service_resp_time_rule: endpointName IN (GET /api/user) AND resp_time > 1000 | 当特定端点响应时间超过 1000ms 触发告警。 |
| 服务实例规则 | instance_jvm_memory_rule: serviceName = "order-service" AND instance_jvm_memory > 80 | 当服务实例 JVM 内存使用率超过 80% 触发告警。 |
| 端点规则 | endpoint_error_rate_rule: endpointName = "/api/pay" AND error_rate > 5 | 当支付接口错误率超过 5% 触发告警。 |
| 服务关系规则 | service_relation_resp_time_rule: destServiceName = "user-service" AND resp_time > 2000 | 当调用用户服务的响应时间超过 2000ms 触发告警。 |
| 指标阈值 | 支持 resp_time, error_rate, p99, p95, qps, heap_memory, cpu 等。 | 可组合多个条件(AND/OR)。 |
| 告警周期 | period: 5(评估周期为 5 分钟) | 在每个周期内指标持续满足条件才触发。 |
| 持续时间 | count: 3(连续 3 个周期满足条件) | 避免瞬时抖动导致误报。 |
| 忽略时间 | silence_period: 3(告警后 3 个周期内不再重复触发) | 防止告警风暴。 |
| 配置文件位置 | config/alarm-settings.yml | 修改后 OAP Server 需要重启或热加载(取决于版本)。 |
7.4 告警通知渠道配置(Webhook, 邮件等)
| 通知渠道 | 配置方法 | 注意事项 |
|---|---|---|
| Webhook | 在 alarm-settings.yml 中配置 Webhook URL 和消息模板。 | 可集成企业微信、钉钉、飞书、Slack 等。 |
| 邮件 (Email) | 配置 SMTP 服务器、发件人、收件人列表。 | 需提供 SMTP 地址、端口、用户名、密码。 |
| gRPC | 配置 gRPC 服务端地址,SkyWalking 通过 gRPC 发送告警数据。 | 适用于自定义告警处理服务。 |
| Slack | 提供 Slack Webhook URL 或使用 Bot 集成。 | 需在 Slack 中创建 Incoming Webhook。 |
| 钉钉 (DingTalk) | 使用自定义机器人 Webhook,支持加签。 | Webhook URL 需包含 access_token,可设置安全加签。 |
| 企业微信 (WeCom) | 使用群机器人 Webhook。 | 支持文本、Markdown 消息格式。 |
| 飞书 (Lark) | 使用自定义机器人 Webhook。 | 类似钉钉,支持富文本消息。 |
| 消息模板 | 可自定义告警消息内容,包含服务名、实例、指标、阈值、时间等变量。 | 确保信息清晰,便于快速响应。 |
| 多渠道通知 | 可同时配置多个渠道,确保告警不遗漏。 | 例如:邮件 + 钉钉。 |
| 安全性 | 避免在配置中硬编码敏感信息(如密码),使用环境变量或密钥管理服务。 | 部分版本支持从环境变量读取配置。 |
第八章:生产环境最佳实践
8.1 Agent 性能开销评估与调优
| 优化项 | 配置建议 | 效果 |
|---|---|---|
| 采样率 (Sampling) | 设置 agent.sample_n_per_3_secs=1 或使用百分比采样。 | 降低数据量,减少网络和存储开销。 |
| 批处理大小 | 调整 collector.backend_service 的批量发送大小和间隔。 | 平衡延迟与吞吐量。 |
| 日志级别 | 生产环境使用 INFO 或 WARN,避免 DEBUG。 | 减少 Agent 自身日志开销。 |
| 禁用不必要的插件 | 在 agent.config 中将 plugin.xxx.enable=false。 | 如禁用未使用的 DB、MQ 插件,减少字节码增强开销。 |
| 内存限制 | 为 Agent 分配合理内存,避免频繁 GC。 | 通常默认配置已足够,高负载服务可适当增加。 |
| 异步上报 | 确保使用 BatchSpanProcessor 而非 SimpleSpanProcessor。 | 减少对应用线程的阻塞。 |
| 监控 Agent 开销 | 使用 JVM 监控工具(如 JConsole)观察 Agent 对 CPU 和内存的影响。 | 评估性能影响,确保 < 5%。 |
| 升级到最新版本 | 新版本通常包含性能优化和 Bug 修复。 | 定期评估升级。 |
| 避免高频日志埋点 | 不在循环或高频调用中创建 Span 或记录事件。 | 防止产生海量 Span 导致 OAP 压力过大。 |
8.2 安全配置(认证、加密)
| 安全措施 | 配置方法 | 说明 |
|---|---|---|
| OAP 通信加密 | 启用 TLS/SSL,配置 collector.grpc.ssl.* 和 collector.rest.ssl.*。 | 保护 Agent 与 OAP 之间的数据传输。 |
| 认证 (Authentication) | 在 OAP 端启用 JWT 或 Basic Auth,Agent 配置相应 token 或凭证。 | 防止未授权 Agent 上报数据。 |
| Agent 配置加密 | 敏感配置(如数据库密码)使用加密,Agent 启动时解密。 | SkyWalking 本身不直接支持,需结合外部方案。 |
| 网络隔离 | 将 OAP Server 部署在内网,通过防火墙限制访问。 | 仅允许 Agent 所在服务器访问 OAP 的 11800/12800 端口。 |
| RBAC (基于角色的访问控制) | 在 UI 层(如通过 Nginx 或前端网关)实现用户权限管理。 | 控制不同用户对监控数据的查看和操作权限。 |
| 数据脱敏 | 在插件中对敏感信息(如用户 ID、手机号)进行脱敏处理后再上报。 | 避免隐私数据泄露。 |
| 定期审计 | 审查 Agent 配置、OAP 日志和访问记录。 | 及时发现安全风险。 |
| 更新依赖库 | 及时更新 Agent 和 OAP 的第三方库,修复已知漏洞。 | 使用 dependency-check 等工具扫描。 |
8.3 多环境(Dev/Test/Prod)配置管理
| 环境 | Agent 配置建议 | OAP 配置建议 | 数据存储建议 |
|---|---|---|---|
| 开发 (Dev) | - 高采样率或全量采集 - 启用所有插件 - 日志级别 DEBUG - 上报到独立 OAP | - 使用轻量级存储(如 H2) - 开启调试日志 - 允许更多探针连接 | 本地或开发环境专用存储,数据可定期清理 |
| 测试 (Test) | - 中等采样率 - 启用核心插件 - 日志级别 INFO - 上报到测试 OAP | - 使用独立集群 - 存储保留周期较短 - 配置基本告警 | 测试专用 ES/MySQL 实例,与生产隔离 |
| 生产 (Prod) | - 低采样率 - 仅启用必要插件 - 日志级别 WARN - 启用 TLS 加密和认证 | - 高可用集群 - 大容量存储 - 严格的安全策略 - 配置关键业务告警 | 独立、高性能、高可用的存储(如 ES 集群) |
| 通用策略 | - 使用 -Dskywalking.agent.service_name=service-name-env 区分环境- 通过环境变量或配置中心管理配置 - 使用 Ansible/Puppet 统一部署 | - 通过 namespace 或 service_name 前缀区分环境数据- 使用不同的索引/数据库 | 严格隔离,禁止跨环境访问 |
8.4 高可用部署 OAP Server
| 部署组件 | 高可用方案 | 说明 |
|---|---|---|
| OAP Server 集群 | 部署多个 OAP 实例,通过负载均衡(如 Nginx、HAProxy)对外提供服务。 | Agent 上报数据可被任意实例接收,后端存储需共享。 |
| 后端存储 | - Elasticsearch: 部署 ES 集群(多节点、多副本) - MySQL: 主从复制或使用云数据库高可用版 | 存储是 OAP 的核心依赖,必须高可用。 |
| ZooKeeper / Consul | 用于 OAP 集群的协调和服务发现(部分部署模式需要)。 | 如使用 gRPC 接收器时,Agent 可通过服务发现找到 OAP 实例。 |
| 数据一致性 | OAP 无状态,所有实例连接同一后端存储,数据一致性由存储层保证。 | 确保存储的写入性能和可靠性。 |
| 负载均衡 | 在 OAP 前部署 LB,Agent 配置 LB 地址作为上报端点。 | 支持 gRPC 和 HTTP 流量。 |
| 健康检查 | 配置 LB 的健康检查路径(如 /v3/health)。 | 及时剔除故障节点。 |
| 自动伸缩 | 根据 CPU、内存或消息队列积压情况,对 OAP 实例进行水平扩展。 | 适用于流量波动大的场景。 |
| 容灾备份 | 定期备份 ES/MySQL 数据,制定灾难恢复计划。 | 确保监控数据不丢失。 |
| 监控 OAP 自身 | 使用外部监控系统(如 Prometheus + Grafana)监控 OAP 的 JVM、GC、队列等指标。 | 及时发现 OAP 性能瓶颈或故障。 |