ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

构建实时监控系统:从指标采集到智能告警的工程实践

构建实时监控系统:从指标采集到智能告警的工程实践 1. 项目定位与整体思路先解释一下这个项目是干什么的。PLFM_RADAR全称是我自己起的 Platform Radar本质是一套面向平台的实时运行监测系统。它做的事情很聚焦把散落在各个服务里的日志、指标、调用链路信息收拢起来用类似雷达扫描的方式持续观察平台的流量特征和健康状态一旦发现异常信号就主动暴露出来而不是等其他系统先报错、用户先投诉。这套东西最核心的价值是把“事后翻日志排查”变成“事中看雷达定位”。日常运维里我们其实不缺监控工具缺的是能根据平台自身流量基线自动判断“什么算异常”的判断力。云厂商的告警也好、自建监控也好大多基于固定阈值比如CPU超过80%、错误率超过5%。但实际业务根本没那么听话一个活动流量瞬间翻十倍请求量大涨不一定是坏事某些接口深夜在线人数骤降也不代表服务挂了可能只是大家都睡了。固定阈值的告警要么一天响八百次全是误报要么漏掉真正的问题。PLFM_RADAR在设计上借鉴了雷达的思维方式先建立正常海域的航迹模型一旦发现航迹偏离、速度突变、目标消失才判定为需要关注的异常。所以这套系统不只是采集数据、画几张图表而是天然地把“趋势判断”“波动识别”和“告警抑制”放在核心位置。适合需要维护多个业务模块、多条调用链路、并且对稳定性有较高要求的中小规模技术团队参考。我自己就是从一台云主机、三个后端服务、一个前端页面的极简架构起步的做到后面逐步覆盖了十几个服务节点和几十个接口维度。整个过程踩了不少坑也换过几次方案。这篇博文会从整体设计、关键模块实现、踩坑记录到优化方向完整过一遍力求让你看完之后能直接照着搭一套而不是只停留在概念层面。2. 核心设计指标、管道与存储选型2.1 到底要盯哪些指标很多团队第一步就栽在指标选择上。一上来接几十个监控项看板拉出来五花八门最后真正出问题时反而不知道看哪里。我觉得应该少而精先盯住四类信号。第一类是流量信号包括每秒请求数、活跃用户数、在线连接数。这类指标直接反映平台当前承受的压力也是判断流量异常最基础的数据源。第二类是性能信号重点关注接口平均响应时间、P95/P99延迟、数据库慢查询数。P95比平均值要诚实得多平均响应时间200毫秒背后可能是九成请求都很快、少数几个请求卡了十几秒。第三类是成功率信号包括HTTP 5xx比例、业务异常率、超时次数。第四类则是基础设施信号比如CPU使用率、内存占用、GC暂停时间、磁盘IO等待。PLFM_RADAR的指标定义思路是给每个指标加上“平台维度”的标签。例如订单服务这个平台下的“创建订单接口”既要看它自身的错误率也要看它依赖的库存服务、支付回调的响应情况。所以每条指标数据不只是一个数字而是一个有上下文的元组。采集时统一带上platform_id、service_name、endpoint、metric_name四个维度做聚合和告警时再按不同粒度切分。这样做的好处是分析问题时可以在“平台总览”和“具体链路”之间随意下钻不存在数据口径对不上的情况。另外还有一个很容易被忽略的指标队列积压量。只要平台里有异步任务线程池排队数量、消息队列积压数量就非常能说明问题。积压量从几百突然涨到几万往往意味着某个下游消费者卡住了而这种故障在API错误率上不一定立刻体现出来。2.2 数据管道怎么搭数据管道解决的是“指标从哪里来、怎么传到存储”的问题。刚开始我就用最简单粗暴的方式在各个服务里埋点直接把统计数据POST到中心服务中心服务预处理后写入数据库。这种方案在服务数量少的时候没问题但服务一多、数据量一上来最直接的问题就是会阻塞业务线程。某些服务用的是同步HTTP调用数据上报超时了反而把正常接口拖慢了典型的监控系统影响业务系统。后来我调整成了标准的三段式管道采集端 → 缓冲队列 → 消费处理。采集端仍然是基于SDK埋点但上报动作改成异步批量提交缓冲层用的是Kafka按主题拆分成原始日志流和指标流下游起独立的消费服务从Kafka拉数据、做清洗、聚合再写入存储。这里可能有人会问为什么非要多加一层Kafka直接把数据打到存储不行吗原因很简单缓冲层能削峰填谷。线上流量是突发的凌晨三点可能每秒只有几十条指标到了早上十点可能每秒几千条。如果下游存储直接对采集端暴露突发流量一来写入就会开始堆积慢查询、超时跟着出现。而Kafka能扛住这种短时突发消费端可以按自己的节奏处理数据哪怕消费速度暂时跟不上数据也不会丢。如果团队规模比较小不想引入Kafka也可以先用Redis的List结构或者Stream结构做简易缓冲但要注意做好数据积压监控和过期清理。我个人的建议是一旦日均指标条数超过百万级别Kafka带来的复杂度收益就是值得的。2.3 存储选型与字段设计存储这块我前后换过两个方案正好可以分享对比一下。第一版用的是普通关系型数据库把指标数据一行行往表里插。用了一周就不行了数据量增长太快是其次最主要的是查询体验非常糟糕。想看某个接口最近一周的每分钟趋势得写复杂的GROUP BY语句时间范围大了以后一个图表接口要等好几秒才返回这在监控场景下基本不可用。监控数据的本质是时间序列数据它的特点是“只有追加、很少修改、按时间范围查询”关系型数据库不是干这个的。第二版切到了时序数据库用的是InfluxDB。字段设计很直接measurement就是metric_nametag是一组固定维度field是数值。比如一条指标数据在InfluxDB里长这样request_count,platformorder_svc,serviceorder_api,host10.0.0.11 value1523 1700000000000000000measurement是request_counttag组合是platformorder_svc、serviceorder_api、host10.0.0.11field是实际数值最后一个字段是纳秒级时间戳。时序数据库会自动按时间做分片和索引连续查询也多。窗口统计、降采样这些都是内置能力写起来很舒服。如果数据量再大一档比如每秒几万条指标InfluxDB单机版本可能不够稳。社区里更常见的做法是直接上ClickHouse配合物化视图做预聚合。我目前停留在InfluxDB单机加连续查询的状态日均指标量在千万级别还算够用。选型的关键是量级匹配没必要一开始就上大集群但设计字段时要预留扩展空间。2.4 选型背后的几个原因再补充一些选型思考。为什么用Kafka而不是直接用HTTP推送到中心因为数据管道和数据存储需要的可靠性不一样。Kafka对消息的持久化和消费位置管理是成熟方案写进去的数据不容易丢上游推送HTTP数据则完全依赖中心服务进程状态进程一重启数据就断了。而使用InfluxDB而不是Elasticsearch是因为ES擅长的是全文检索指标数据的数值聚合查询用ES做比较别扭内存开销也大。时序数据库天生就是干这个的。整体架构最终长这样服务内嵌SDK异步上报 → Nginx或者Kafka接收 → 消费进程清洗聚合 → InfluxDB存储 → 告警引擎扫描 → 可视化面板展示。这套架构不算新但胜在简单可靠。核心逻辑都在消费进程里没有一堆微服务互相调来调去。我从一开始就刻意避免把系统拆得太碎毕竟监控系统自身的稳定性也是要重点保障的。3. 关键模块实现从采集到告警3.1 采集端不该为了“全”而牺牲业务性能采集端的实现原则第一条不能影响业务主链路。我见过有人直接在接口里写同步上报代码结果下游慢了几百毫秒整个接口的延迟都被拖高。采集端的正确做法是异步批处理合并上报。我的SDK逻辑其实很简单内部维护一个环形缓冲队列业务线程只需要把指标对象塞进去就返回完全不等待网络IO。后台有一个独立发送线程每隔5秒或者攒够500条就把缓冲里的数据压缩后批量POST到接收端。发送失败的重试策略也很关键我采用的是有限次数重试加本地落盘兜底避免无限重试导致线程堆积。字段设计上面已经说过了这里直接给一个具体结构{ ts: 1700000000, platform: order_svc, service: order_api, endpoint: /v1/order/create, host: 10.0.0.11, metric: p95_latency, value: 182.5, trace_id: samplespan12345 }这个设计的好处是原始结构和最终存储结构几乎一一对应消费端做清洗时不需要做复杂的字段映射。trace_id字段是后来加的目的是当某个时间段内指标异常时能直接根据时间窗口关联到调用链系统快速定位到具体的错误Trace。监控数据和链路追踪打通以后排查效率提升非常多。建议埋点的时候别一股脑把所有指标都上报。流量小的接口可以全量埋流量大的接口最好只保留关键指标或者做采样上报。我之前有个用户行为埋点接口QPS每秒上千结果连续上报几小时后监控系统本身的写入量比业务系统还大。3.2 滑动窗口统计与基线算法拿到原始指标之后最重要的任务是计算各时间窗口的聚合值以及和基线对比得出偏离程度。我用的窗口是1分钟粒度加5分钟滑动窗口。每分钟生成一条数据同时统计最近5分钟内请求总量、平均延迟、P95延迟等。之所以用滑动窗口是因为它能平滑毛刺。某个接口单次P95延迟飙到1.2秒可能只是个别慢请求但如果连续几个窗口都偏高那就说明问题持续存在了。基线算法是PLFM_RADAR的核心部分。我最初用的是最简单的周同比拿当前1小时的数据和上周同一时段对比如果偏差超过30%就判定异常。效果有但非常粗糙因为业务流量每周本来就在波动活动、版本发布也会导致基线漂移。后面改成了“动态基线”方案每天把相同时间段的历史数据记录下来连续保留28天算平均值和标准差。判定时用当前值与历史均值的差除以历史标准差得到偏离倍数。偏离倍数超过3就判定为强异常信号。用标准差而不是固定百分比算是这个项目里最有价值的调整。比如某个接口的正常抖动范围是5%那么偏差30%是异常但另一个接口由于定时任务集中执行正常抖动就是30%那偏差30%其实完全正常。用标准差就能自动适应各指标自身的历史波动区间。3.3 告警引擎触发、抑制与收敛告警是这套系统最容易翻车的地方告警配置不合理比不配置还糟。我见过最夸张的情况一个平台一天收到上千条告警短信最后所有人把告警群一拉黑真出问题时反而没人管了。我的告警引擎设计分成三层第一层是规则触发规则由四部分组成指标表达式、时间窗口、阈值条件和持续时间。比如“request_count5分钟窗口偏离基线倍数3持续2个窗口触发告警”。这里的持续时间条件非常重要它能过滤掉瞬时抖动只有连续多个窗口异常才告警。第二层是告警抑制同一个指标、同一个维度在30分钟内只发一次告警避免同一问题反复轰炸。第三层是告警收敛针对同一条调用链路的多条告警按平台维度折叠成一条汇总信息把关联指标一并带上。告警通知渠道我最终只保留了企业微信机器人和邮件。企业微信适合第一时间快速响应附上告警指标数值、触发时间、当前值、基线参考值邮件则是留痕方便事后复盘。短信不推荐一是不够灵活二是费用也不低如果必须用只保留最高级别的故障告警走短信通道。还有一个很实用的小功能告警自助屏蔽。在版本发布窗口内大部分指标出现波动是预期内的所以我提供了基于时间段的临时屏蔽规则比如“2025年1月10日20:00到22:00屏蔽order_svc平台所有延迟类告警”。如果不做这一层每次发版都触发一堆无意义告警运维同学对告警的信任度会快速下降。3.4 可视化面板别做“数据橱窗”可视化面板是给团队用的不是给自己展示技术用的。所以我在设计看板时坚持几个原则一屏只回答一个问题指标不要堆砌能看趋势的优先用趋势图不要用数字卡片异常时段必须自动标记让观察者第一眼就看到问题区域。我的主要看板分成四块。第一块是平台总览展示最近一小时的请求总量曲线、平均延迟曲线、错误率曲线叠加异常事件标记。第二块是接口列表按P95延迟或者错误率排序方便快速找到最差的端点。第三块是链路依赖图图形化展示服务之间的调用关系以及每条边的错误率变化。第四块是告警历史近7天触发过的每条告警都能在这里回看方便交接班复盘。Grafana是现成的可视化方案接InfluxDB数据源后查询语法虽然要记几个函数名但熟悉之后配置起来很快。我额外写了一点增强功能定时自动把每日报表推送到群里包含平台核心指标趋势摘要和告警统计。这比让团队每个人上班后自己打开看板舒服得多尤其在多团队协同的场景下日报是信息同步效率非常高的方式。4. 上线后踩过的坑与排查实录4.1 时间戳偏差引发的误报第一次误报风波是上线第三天凌晨发生的。告警引擎突然判定某个接口的请求量比基线低了90%触发了“流量低异常”。我登录服务器一看业务进程跑得好好的日志也很正常。排查很久才发现问题出在时间戳上采集端所在服务器系统时间比中心服务器慢了将近3分钟数据写进InfluxDB时带了错误的时间标签。告警引擎按当前时间窗口去查数据查不到自然就认为流量跌到零。这个问题直接让我意识到时间同步是监控系统最容易被忽视的基础设施。所有采集节点必须强制配置NTP定期同步而且最好在上游接收数据时就做一层时间合法性校验凡是时间偏差超过2分钟的数据不落库而是扔到异常队列里人工检查。后来我还在SDK初始化时增加了一个时间校准接口采集端启动后主动向中心对时减少本地时钟漂移。4.2 高基数维度把存储打爆监控系统做得越细标签维度越多隐患也越大。我早期为了追踪每个接口的调用来源在tag里加了client_version字段。客户端版本号少说几十个再叠加platform、service、endpoint三个维度InfluxDB的索引直接涨到吓人的程度单机写入性能大幅下降查询也频繁超时。这是典型的高基数问题。时序数据库对时间维度优化很好但对“每个时间点上的标签组合数”非常敏感。每增加一个高基数标签就等于把一个指标变成了几十个独立序列。后来我把client_version从tag移到了field只保留最近一个版本的信息用于调试不再参与索引和聚合。同时新增了强制约束可作为tag的字段组合基数必须控制在百万级以内超过阈值要么移除该字段要么做分桶降基数。4.3 告警风暴是怎么收住的第一次遇到真实故障时告警风暴的问题彻底爆发了。某个核心数据库的连接数满了结果依赖它的三个服务的所有API都开始超时。因为每个API超时都会触发错误率告警我数了一下一分钟内收到了90多条告警消息群里全程刷屏真正有价值的第一条故障信号反而被淹没了。这就是我前面讲抑制策略的由来。我加了两条规则一是同一个服务维度在1小时内最多只发出3条告警后续告警进入静默列表但记录日志二是检测到下游某节点异常时上游所有关联错误类告警自动折叠为一条“上游依赖异常”提示不再逐个展开。这样处理以后同样规模的故障告警数量从90条降到了3条一条核心库连接数告警、一条服务可用性告警、一条依赖异常汇总提示。有种做法我很不推荐单纯靠“提高触发阈值”来减少告警数量。那是掩耳盗铃真实故障依然在只是告警不响了。正确方向是提高告警的信息密度和上下文关联能力让少而精的告警信息足够支撑快速决策。4.4 写放大与IO抖动监控系统对存储的写入压力其实比大多数业务系统更“匀速且连续”。业务系统的写入高峰是有周期的监控系统则是每分每秒都在写。在数据量上来之后InfluxDB所在磁盘的IO等待时间开始明显拉高进而拖慢了查询接口甚至出现了数据落盘延迟。排查发现两个原因一是InfluxDB的存储合并策略比较吃IO资源二是和其他共用同一块数据盘的中间件抢IO。解决办法很简单给InfluxDB单独挂一块SSD数据盘保证读写IO不被其他服务干扰。同时打开InfluxDB的continuous query功能把历史数据做降采样汇总一天前的数据自动从原始精度降为5分钟精度这样数据量随时间增长的速度能被控制住。这个坑给到我的经验是监控系统的性能规划重点不是算峰值流量而是算持续写入量。峰值流量决定设计上限持续写入量决定硬件下限。如果平均值都超过硬件能力的一两成那峰值来了大概率出问题。4.5 几个容易被忽略的小细节再分享几个实操中慢慢积累的小细节。第一个是数据保留策略要明确InfluxDB里的原始数据我只保留15天降采样数据保留90天超期自动清理。磁盘空间一定是提前规划好的不能等到告警才发现磁盘满了。第二个是消费端必须做幂等处理。Kafka消息在某些场景下会被重复消费同一批指标写两次虽然数值不会错但聚合统计会被放大导致异常误判。我在消费进程里基于tsplatformmetric生成了唯一消息ID用内存去重表做短时间范围去重。第三个是告警文本里一定要带当前值和基线值否则收到告警的人还得二次打开看板效率极低还容易误判。第四个是监控系统自身的监控。PLFM_RADAR对每个采集节点会定期检查“最近5分钟是否收到心跳数据”。如果哪个服务连续两次心跳缺失就先发一条级别较低的提醒。监控系统自身挂了可能是最尴尬的故障所以心跳和自检机制必须优先保证。5. 优化方向与个人体会5.1 从“事后告警”到“事前预测”做完目前这套我最大的感受是告警本质上还是“事后”的逻辑。它告诉我们系统出问题了但问题已经发生了。下一步我打算做的方向是把雷达从“扫描异常”升级为“预测趋势”。一个可行的思路是基于时序预测算法比如对历史流量做周期分解用Prophet或者轻量级的STL分解方法预测未来10分钟的趋势区间再把实时数据放进区间比较。如果实际流量明显超出预测区间可以提前几分钟发现问题。这在活动大促场景下尤其有价值流量预测能力可以提前预警系统容量瓶颈。另外让我改进的还有根因定位模块。目前PLFM_RADAR的告警能告诉你“哪里出了问题”但还不能直接告诉你“为什么出问题”。我计划把服务的依赖关系图引入告警链路分析当某条调用链路的错误率告警触发时自动向上游查依赖状态。如果发现依赖的下游服务响应时间明显劣化就把根因候选标注出来。5.2 最值得先做的一件事如果只让我给一个建议那就是别急着上高级功能先保证两件事数据链路是通的告警是可以解释的。数据链路通畅意味着从埋点到存储再到看板的每一步都验证过而不是等故障真来了才发现某个环节断了一星期没人知道。告警可解释意味着每一条告警都能回答三个问题发生了什么、影响范围是什么、当前系统和基线相差多少。PLFM_RADAR这个项目走到现在最大的收获不是指标有多全、算法有多炫而是让我真正体会到监控系统的核心哲学它是给决策提供依据的工具不是一个制造噪音的机器。好的监控系统一定是一线人员在关键时刻觉得“我靠这个能快速判断”而不是“这个系统又在乱叫”。我在实际使用中的体会是一次靠谱的监控告警省下的时间是通常在3到10倍的排查时间。如果你也在做类似平台监控方面的探索完全可以从最简版本起步一台服务器、一个时序数据库、一份采集SDK、一套规则配置先把核心闭环跑起来再逐版迭代增强。这套思路不需要一步到位但每一步都得解决真实问题。
返回列表