
上一篇【第22篇】SkyWalking告警配置实战——让你的系统学会“喊救命“下一篇【第24篇】SkyWalking Kubernetes部署实战——让监控与云原生共舞1. 引言单体应用没有含金量吗如果你在技术大会上提到我们在用Spring Boot单体应用可能会收获一些微妙的微笑。在万物皆可微服务的时代“单体似乎成了一个略带贬义的词——听起来像在说我们还在用功能机”。但现实是什么根据各种技术调研报告超过60%的中小企业系统仍然是单体架构。一个团队8个人维护12个微服务光是DevOps成本就够喝一壶的了。很多时候单体应用不是技术债务而是一个务实的选择。更重要的是——单体应用的监控难度一点都不比微服务低。一个巨大的WAR包里可能有200个Controller、50个Service、30个DAO一个请求进来经历了多少层调用你确定你清楚今天我们要讨论的就是SkyWalking怎么帮单体应用挖出隐藏的性能问题。2. 单体应用的特点与监控痛点2.1 什么是真正的单体应用------------------------------------------------------------------ | 单体应用典型架构 | ------------------------------------------------------------------ | | | ┌──────────────────────────────────────────────────────────────┐ | | │ MySuperApp.war (1.2GB) │ | | │ │ | | │ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │ | | │ │ 订单模块│ │ 用户模块│ │ 商品模块│ │ 支付模块│ │ | | │ └───┬────┘ └───┬────┘ └───┬────┘ └───┬────┘ │ | | │ │ │ │ │ │ | | │ └────────────┴────────────┴────────────┘ │ | | │ │ │ | | │ ┌─────────┴─────────┐ │ | | │ │ 共享Service层 │ │ | | │ └─────────┬─────────┘ │ | | │ │ │ | | │ ┌─────────┴─────────┐ │ | | │ │ 共享DAO/数据层 │ │ | | │ └─────────┬─────────┘ │ | | │ │ │ | | └────────────────────────┼─────────────────────────────────────┘ | | │ | | ┌────────────┼────────────┐ | | ▼ ▼ ▼ | | ┌─────────┐ ┌─────────┐ ┌─────────┐ | | │ MySQL │ │ Redis │ │ 外部API │ | | └─────────┘ └─────────┘ └─────────┘ | ------------------------------------------------------------------2.2 单体应用的三大监控痛点痛点描述传统解法SkyWalking解内部调用链路不透明一个HTTP请求进来经过Filter→Controller→ServiceA→ServiceB→ServiceC→DAO到底哪一层慢了人肉加System.currentTimeMillis()Trace自动拆解每一层数据库慢查询发现难200个Mapper方法哪几个SQL是慢的需要翻遍slow_query_logDBA定期导出慢查询日志在Trace中直接关联SQL耗时外部服务不可见调了第三方短信接口、支付网关、云存储超时了也不知道靠用户投诉ExitSpan记录每次外部调用的完整信息3. SkyWalking在单体应用中的价值3.1 接口级别的全貌监控即使只有一个JVM进程SkyWalking也能帮你看清每个Controller的真实表现------------------------------------------------------------------ | SkyWalking 端点面板 - 单体应用视图 | ------------------------------------------------------------------ | | | ┌────────────────────────────────────────────────────────────┐ | | │ 端点 | 平均RT | P99 | 调用量 │ | | ├────────────────────────────────────────────────────────────┤ | | │ /api/order/create {POST} | 320ms | 1.2s | 2,340/day │ | | │ /api/order/list {GET} | 890ms | 3.5s | 8,900/day │ | | │ /api/user/login {POST} | 150ms | 450ms | 12,000/day │ | | │ /api/report/export {GET} | 8.2s | 15.0s | 120/day │ | | └────────────────────────────────────────────────────────────┘ | | | | /api/report/export 导出报表耗时8.2秒 — 需要立即优化 | | /api/order/list 订单列表P99达3.5秒 — 需要关注 | | /api/user/login 登录性能良好 | ------------------------------------------------------------------3.2 案例诊断一个薛定谔的慢接口某单体电商系统运维反馈/api/product/detail接口有时候很快有时候很慢。不规律不好复现。Step 1SkyWalking端点面板看趋势发现该接口的响应时间呈双峰分布大部分请求100ms以内返回但约有15%的请求耗时超过2秒。Step 2挑一个慢请求看TraceTrace: /api/product/detail?id12345 (总耗时 2.3s) ├── [EntrySpan] ProductController.detail() (2.3s) │ ├── [LocalSpan] ProductService.getDetail() (2.28s) │ │ ├── [ExitSpan] Redis GET product:detail:12345 (5ms) ✅ │ │ ├── [ExitSpan] MySQL SELECT * FROM products (45ms) ✅ │ │ ├── [ExitSpan] HTTP GET stock-service/stock/12345 (2.2s) ⚠️ │ │ │ └── 外部库存服务超时连接正常但响应极慢 │ │ └── [LocalSpan] 组装数据 (8ms) ✅ │ └── [LocalSpan] 返回视图 (2ms) ✅Step 3根因定位同一个商品ID的数据库和缓存都正常问题出在外部库存服务。进一步分析发现当商品库存大于1000时热销品库存服务的批量计算逻辑会全表扫描——而且这个逻辑只在少数热销品上触发所以只有15%的请求受影响。这就是单体应用中SkyWalking的典型价值场景——虽然只有一个进程但调用链的复杂度藏在代码里。3.3 单体应用Top 5慢查询SkyWalking会自动统计数据库访问帮你生成单体应用慢查询清单-- SkyWalking数据库面板显示的Top 5慢SQL1.SELECT*FROMordersWHEREstatuspendingORDERBYcreated_at-- 平均耗时 1.2s缺少(status, created_at)联合索引2.SELECTo.*,oi.*,p.*FROMorders oLEFTJOINorder_items oiONo.idoi.order_idLEFTJOINproducts pONoi.product_idp.idWHEREo.user_id?-- 平均耗时 850ms三表联查没有覆盖索引3.UPDATEinventorySETstockstock-?WHEREproduct_id?-- 平均耗时 200ms行锁等待4.SELECTCOUNT(*)FROMoperation_logWHEREcreated_at?-- 平均耗时 150ms日志表缺少分区5.SELECT*FROMproductsWHEREnameLIKE%手机%LIMIT20-- 平均耗时 120msLIKE前缀通配无法用索引4. Agent对单体应用的影响评估“接Agent会不会让我的单体应用变慢”——这是几乎所有团队的第一反应。来我们用数据说话。4.1 SkyWalking Agent的性能开销------------------------------------------------------------------ | SkyWalking Java Agent 性能开销评估 | ------------------------------------------------------------------ | | | ┌─────────────────┬──────────┬─────────────────────────┐ | | │ 开销类型 │ 典型值 │ 说明 │ | | ├─────────────────┼──────────┼─────────────────────────┤ | | │ CPU开销 │ 1-5% │ 字节码增强的额外计算 │ | | │ 内存开销 │ 50-200MB│ Buffer/Channel内存占用 │ | | │ 启动延迟 │ 1-3s │ 类加载时字节码增强 │ | | │ 请求延迟增加 │ 0.1ms │ 每个Span的创建/上报开销 │ | | │ GC压力 │ 极低 │ DataCarrier零GC设计 │ | | │ 网络开销 │ 可忽略 │ gRPC批量上报压缩传输 │ | | └─────────────────┴──────────┴─────────────────────────┘ | | | | ⚠️ 注意高吞吐场景10000 QPSCPU开销可能升至5-10% | | 建议通过采样率控制默认100%采样可调低 | ------------------------------------------------------------------4.2 压测对比数据某电商单体的实测以下数据来自某Spring Boot电商单体应用4C8G的云服务器------------------------------------------------------------------ | 有无Agent的性能对比 | ------------------------------------------------------------------ | | | 测试场景: 100并发, 持续60秒, /api/product/list | | | | ┌────────────────┬────────────┬────────────┬────────┐ | | │ 指标 │ 无Agent │ 有Agent │ 差异 │ | | ├────────────────┼────────────┼────────────┼────────┤ | | │ TPS │ 2450 │ 2320 │ -5.3% │ | | │ 平均响应时间 │ 41ms │ 43ms │ 4.9% │ | | │ P99响应时间 │ 120ms │ 128ms │ 6.7% │ | | │ P999响应时间 │ 350ms │ 380ms │ 8.6% │ | | │ CPU使用率 │ 65% │ 70% │ 5% │ | | │ 堆内存使用 │ 2.1GB │ 2.3GB │ 200MB│ | | └────────────────┴────────────┴────────────┴────────┘ | | | | 结论整体性能损耗在5-8%之间属于可接受范围 | ------------------------------------------------------------------4.3 Agent采样率配置如果你的单体应用QPS很高可以通过采样率来控制开销# agent/config/agent.config# 采样率每N个请求采样一个-1全采样1全采样250%采样agent.sample_n_per_3_secs1# 或者按服务名配置采样率plugin.tomcat.collect_http_paramstrue# 启动参数方式java-javaagent:/path/to/skywalking-agent.jar\-Dskywalking.agent.sample_n_per_3_secs2\-jarmyapp.jar5. 单体→微服务迁移的监控过渡方案5.1 迁移的经典路径------------------------------------------------------------------ | 单体到微服务迁移路线图 | ------------------------------------------------------------------ | | | 阶段1: 纯单体 阶段2: 逐步拆分 | | ┌──────────────────┐ ┌─────────┐ | | │ MySuperApp │ │ Monolith│ | | │ 所有模块在一起 │ │ (缩减版) │ | | └──────────────────┘ └────┬────┘ | | │ | | │ 拆出 | | ▼ | | ┌─────────────────┐ | | │ order-service │ | | │ (新建微服务) │ | | └─────────────────┘ | | | | 阶段3: 持续拆分 阶段4: 全微服务 | | ┌─────────┐ ┌─────────┐ | | │ Monolith│ │ order │ | | │ (核心) │ │ service │ | | └────┬────┘ └─────────┘ | | │ ┌─────────┐ | | ├─────────────────────────────│ user │ | | │ │ service │ | | │ └─────────┘ | | │ ┌─────────┐ | | └─────────────────────────────│ product │ | | │ service │ | | └─────────┘ | ------------------------------------------------------------------5.2 SkyWalking在迁移过程中的独特价值迁移前阶段1用SkyWalking识别模块间的调用热力图谁调谁一目了然找出模块边界为拆分提供数据依据迁移中阶段2-3同一个Trace横跨单体和微服务跨进程调用不再断链评估拆分后性能变化原来模块内调用2ms变成RPC调用20ms这个tradeoff需要有数据支撑迁移后阶段4全链路追踪无缝衔接统一告警配置统一监控标准5.3 过渡期的监控配置策略# 过渡期特殊配置混合架构下的链路追踪# 1. 确保TraceId在单体和微服务间传递# Spring Cloud Feign拦截器Component public class FeignTraceInterceptor implements RequestInterceptor{Override public void apply(RequestTemplate template){// SkyWalking Agent会自动注入trace header // 这里只需确保Feign的header转发配置正确即可}}# 2. 区分监控视图# 创建两个Dashboard# - 单体模块监控只看Monolith内部的性能指标# - 全链路监控看整体端到端的追踪链路6. 单体应用Agent配置实战6.1 Agent安装零侵入# 1. 下载Agentwgethttps://dlcdn.apache.org/skywalking/java-agent/9.3.0/apache-skywalking-java-agent-9.3.0.tgztar-xzfapache-skywalking-java-agent-9.3.0.tgz-C/opt/# 2. 启动脚本生产环境推荐#!/bin/bashexportJAVA_OPTS -javaagent:/opt/skywalking-agent/skywalking-agent.jar -DSW_AGENT_NAMEmy-shop-monolith -DSW_AGENT_COLLECTOR_BACKEND_SERVICES192.168.1.50:11800 -DSW_LOGGING_LEVELWARN -DSW_AGENT_SAMPLE_N_PER_3_SECS1 java$JAVA_OPTS-jarmy-shop-app.jar6.2 选择性插件启用单体应用可能使用了各种框架按需启用Agent插件# 选择性启用插件——移动不需要的插件到其他目录cd/opt/skywalking-agent/plugins# 单体应用不需要的微服务插件mkdir-p/tmp/skywalking-disabledmvapm-feign-default-http-9.x-plugin-*.jar /tmp/skywalking-disabled/mvapm-spring-cloud-gateway-*.jar /tmp/skywalking-disabled/mvapm-servicecomb-*.jar /tmp/skywalking-disabled/# 单体应用必需的插件保留# apm-spring-mvc-annotation-*.jar -- SpringMVC监控# apm-jdbc-commons-*.jar -- 数据库监控# apm-jedis-*.jar -- Redis监控# apm-httpClient-*.jar -- HTTP调用监控6.3 排除不需要追踪的端点# 单体应用不需要追踪的内容减少开销-Dskywalking.trace.ignore_path/actuator/**,/health,/metrics/**-Dskywalking.trace.ignore_methodcom.example.HealthChecker.*()7. 单体应用监控的典型排查流程------------------------------------------------------------------ | 单体应用SkyWalking排查决策树 | ------------------------------------------------------------------ | | | 用户反馈系统变慢了 | | │ | | ▼ | | ┌───────────────────────┐ | | │ 打开SkyWalking端点面板 │ | | └───────────┬───────────┘ | | │ | | ┌───────────┼───────────┐ | | ▼ ▼ ▼ | | 所有接口都慢 特定接口慢 间歇性慢 | | │ │ │ | | ▼ ▼ ▼ | | 检查JVM指标 看Trace详情 采样多个 | | (GC/CPU/Mem) ┌────┴────┐ Trace对比 | | │ │ │ │ | | ▼ ▼ ▼ ▼ | | Full GC频繁 SQL慢 外部API慢 特定条件触发 | | →调优JVM →加索引 →加超时 →看Span详情 | | →优化SQL →加熔断 →定位触发条件 | ------------------------------------------------------------------8. 总结单体应用不是二等公民SkyWalking对单体应用的价值一点也不打折扣接口监控200个Controller的性能全貌一览无余慢查询定位SQL执行耗时直接关联到具体业务接口外部调用追踪第三方服务的超时、错误无处遁形迁移护航从拆分前分析到拆分后验证全程数据支撑性能开销5-8%换取全链路可见性这笔账怎么算都划算。下一篇文章我们将把战场从单体服务器搬到Kubernetes集群聊聊SkyWalking在K8s上的部署实战。上一篇【第22篇】SkyWalking告警配置实战——让你的系统学会“喊救命“下一篇【第24篇】SkyWalking Kubernetes部署实战——让监控与云原生共舞