【极简监控·进阶篇】AI助力复刻 Glowroot智能截流,打通 SkyWalking-Local告警的任督二脉
目录
- 零、前言
- 一、 核心理念:致敬 Glowroot,基于 Local 底座的“智能截流”
- 二、 架构师的权衡:为什么用 HTTP 击穿 ClassLoader 隔离墙?
- 三、 拒绝“一刀切”:极其严谨的分层评估模型
- 四、 性能底线:异步分发与“防告警风暴”机制
- 五、 给“告警”做监控:绝对白盒化的自检大盘
- 六、结语:闭环,然后无限迭代
- 七、相关
零、前言
在本专栏前面发布的 《放弃 Prometheus!用 RRD4j 打造纯本地“微型时序库”》 中,我们正式迈入了极简监控的深水区。
面对深水区,我们当时罗列了三大演进方向:
- 告警机制(Alert);
- 业务层指标收集;
- 前端体验与 SOP 串联。
为什么我们最终拍板,优先把精力投入到“告警机制(Alert)”的闭环上?
因为作为研发团队,我们需要一个“内部可控的极速闭环”。业务指标和 SOP 串联牵扯到前端、测试、甚至产品等多团队协作,见效慢;而告警机制,我们可以先在测试环境下静默记录,自己人内部排错,对用户毫无感知。
今天,就带大家看看我们是如何汲取 Glowroot 的精髓,用极其巧妙的“骚操作”,打通探针与业务层壁垒的。
一、 核心理念:致敬 Glowroot,基于 Local 底座的“智能截流”
在前文介绍 Glowroot 时,我们极度推崇它的存储哲学:绝不记流水账,只抓刺客!
如果把所有的 SkyWalking Trace 链路都持久化或告警,不仅磁盘吃不消,报警群也会变成无人问津的“狼来了”。因此,我们的 Alert 机制完全复刻了 Glowroot 的思路:只针对「慢请求」和「错误链路」进行评估、拦截与推送告警。
但请特别注意,能够实现这一目标的绝对前提,是我们已经彻底打通了 《极简模式下 JVM 监控与链路追踪落地思路 (SkyWalking-Local)》 这个底层基座!
|
熟悉 APM 源码的同学都知道,原生 SkyWalking 的数据发送是“碎片化”的,探针一生成 Segment 就会立刻向远端 OAP 发送,Agent 根本不知道这条 Trace 的最终总耗时。
|
而我们魔改的SkyWalking-Local,砍掉了 OAP,让数据在 Agent 内部的本地内存(logfileStatMap)中完成了 Segment 的组装与合并。
|
正是因为拥有了这个“本进程链路已合并完成”的前提,我们的探针才真正具备了“上帝视角”,能够对当前 JVM 产生的完整链路进行统一的耗时计算与异常评估。
|
可以说,如果没有SkyWalking-Local这个极其扎实的前置底座,这种媲美 Glowroot 的“本地智能截流告警”根本无从谈起!
整体交互时序图如下:
二、 架构师的权衡:为什么用 HTTP 击穿 ClassLoader 隔离墙?
要把 Agent 探针层捕获到的慢/错 Trace 数据,推送到我们的应用业务层去处理(入库或发企微),面临着一个史诗级的技术壁垒:ClassLoader 隔离。
懂点 APM 底层的人都知道,SkyWalking 运行在独立的AgentClassLoader中。最初,我们设想按官方规范走 SPI 机制(ServiceLoader)。但经过推演,我们发现这是个维护灾难:
- 业务方写的 SPI 实现类会被打进 Spring Boot 的 Fat JAR 中,而 Agent 根本扫描不到它!
- 如果非要走 SPI,业务团队就必须额外维护一个“瘦 Jar”模块,每次部署还要手动拷进 Agent 的
plugins/目录下,这彻底违背了我们“极简单一制品”的底线。
极简架构师的破局之道:放弃 SPI,转投 Webhook!既然我们在做“单体应用”,那宿主 IP 永远都是127.0.0.1啊!我们直接选择了一条极其返璞归真的路:内部 HTTP 回调。
- Agent 发送端:当拦截到慢/错误 Trace 时,Agent 内部直接发起一个极其轻量的 HTTP POST 请求,打向配置好的本机业务端口(如
http://127.0.0.1:8080/inner/sw/trace-alert)。 - 业务接收端:业务系统中暴露一个普通的
@RestController接口。 - 零类加载器污染:业务逻辑 100% 留在了 Spring Boot JAR 内,想怎么查库、怎么发钉钉,业务团队完全自主掌控。
(相关 Agent 端开源实现,见我们仓库的logfile-reporter-plugin模块:GitHub 传送门)
三、 拒绝“一刀切”:极其严谨的分层评估模型
到底什么是“错”?什么是“慢”?只有写过一线业务的人才知道,这里的规则如果“一刀切”,告警群立刻会变成无人问津的“狼来了”。
为了保证每一次告警都是“有效情报”,我们在 Agent 端设计了一套极其严密且灵活的分层判定模型(TraceEvaluator):
1. 关于“错”的三级雷达(L1/L2/L3):
- L1(协议层):直接扫描协议,只要任意一个 Span 的
isError == true(探针内置识别到的严重异常),立刻捕获。 - L2(HTTP 层):很多业务异常探针无法直接判定为 Error。我们会扫描 Entry Span 的 Tags,一旦发现
http.status_code >= 500,强制捕获。 - L3(自定义层):预留规则,允许按 operationName、url 正则等自定义匹配。
2. 关于“慢”的动态覆盖(支持 URL 正则匹配):
我们将合并后的 Trace 取其 Entry Span 算出精准耗时(durationMs)。并借鉴了 Glowroot 的思路,在配置中支持了极细粒度的动态规则覆盖。
比如:默认全局慢请求是 3 秒;但如果是复杂的导出接口,我们可以配置match_url_regex=.*/export/.*,阈值放宽到 60 秒。不仅配置极度灵活,而且匹配优先级遵循正则 > 组件 > 全局,彻底杜绝了无意义的误报骚扰。
四、 性能底线:异步分发与“防告警风暴”机制
把告警做出来不难,难的是在海量并发的生产环境下,告警机制绝不能反噬拖死 SkyWalking 自己的数据处理线程。
为此,在 Agent 的 Dispatcher(分发器)底层,我们硬核植入了两个保护机制:
单线程隔离调度:HTTP 的发送绝不会阻塞 SkyWalking 原本的
DataCarrier消费线程。我们在内部创建了一个单线程的ExecutorService(结合有界队列)专门负责发送 Webhook,哪怕业务端接口短暂卡死,也绝不影响 Agent 的核心主链路采集。ConcurrentHashMap 精准去重:在一个包含几十个 Segment 的庞大 Trace 中,如果发生了 5 个异常,绝不能给业务端发 5 条重复告警!我们利用
ConcurrentHashMap<traceId, AlertState>作为去重位图。系统确保:同一条 TraceID 下的同一类型异常(ERROR|SLOW),有且仅会通知一次!将告警风暴彻底扼杀在 Agent 的内存里。
五、 给“告警”做监控:绝对白盒化的自检大盘
作为一个负责任的基建团队,当我们引入了一个“内部 HTTP 异步分发机制”时,我们必须回答一个问题:这个分发机制自己会不会挂?有没有丢数据?
一如既往,我们坚持“可观测性至上”,借助前端页面将底层的HttpTraceAnomalyListener运行状态彻底白盒化。请看下方我们利用 AI 快速构建的 Web 监控大盘:
这套大盘的含金量在哪?
- 全局状态了如指掌:运维人员或研发打开页面,一眼就能看到:一共分发了多少次告警?被去重跳过了多少次?HTTP 投递成功率是不是 100%?
- 动态配置透明化:当前生效的慢请求阈值(如 3000ms)、Webhook 解析地址清清楚楚。
- 数据怎么来的?正如使用说明里指出的,业务层通过统一的 Toolkit 入口(
SWLogfileXxUtils.statisticStatus().traceAlert),直接跨越鸿沟,从 Agent 内存中拉取这些极其珍贵的自监控统计指标。
六、结语:闭环,然后无限迭代
打通了这条 Agent 到业务层的任督二脉,我们的 Alert 流程宣告彻底闭环。
这套方案没有引入 Kafka,没有引入庞大的规则引擎。我们用一个127.0.0.1的内部 HTTP 调用,就解决了探针报警的数据流转问题。
- 现阶段,业务侧收到告警后,我们先执行“静默日志记录(Log Only)”。在测试环境跑一段时间,验证阈值的精准度。
- 下一阶段,我们将根据试运行情况,开启有选择性的持久化(写入数据库或发送企微通知)。
七、相关
- 【极简监控】告别 OAP 与 ES!一个 Agent 搞定全链路与 UI,探秘单体 APM 界的“核潜艇” Glowroot
- 【专栏导读】拒绝过度设计!零运维成本打造单体Java应用的“铁桶级”极简监控体系
- 【极简监控】告别沉重的OAP!一款专为单体应用打造的 SkyWalking 轻量级本地化 Reporter 插件
