ARTICLE DETAIL

资讯详情

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

从零构建平台雷达:主动探测、状态研判与告警收敛的自动化监控系统实践

从零构建平台雷达:主动探测、状态研判与告警收敛的自动化监控系统实践 1. 项目到底在解决什么问题1.1 平台雷达这名字不是随便起的PLFM_RADAR拆开看就是 Platform Radar平台雷达。干运维或者平台开发的朋友应该一眼就能get到我要做什么——不是做一个监控大盘完事而是做一个真正有雷达感觉的系统主动扫、持续探、发现苗头就报警。过去我们团队维护着好几个业务平台每个平台都有自己的管理后台、开放接口、定时任务和对接的外部服务。表面上看着都挺正常但说实话真正出问题的时候从来不是整个平台挂了这种大动静而是那种悄无声息的恶化——某个接口响应从200ms慢慢涨到2s某个定时任务连续三天延迟执行某个第三方回调接口隔三差五超时。这些问题用传统监控工具不是不能发现而是发现的时候往往已经影响业务了。我当时就想要一个这样的东西它可以周期性地从外部视角去探测这些平台的健康状况像雷达扫描一样不停地转一旦发现任何指标的异常波动立刻通知到人。而且它的部署要足够轻不能为了监控一个平台又引入一套重量级的监控全家桶。这就是 PLFM_RADAR 的初衷。1.2 它跟传统监控有什么不一样你要是用过 Zabbix、Prometheus 这类工具可能会问这些东西不是已经能干这个事了吗确实能但我在实际用下来有几个痛点始终绕不开。最尴尬的一点是监控视角问题。传统的Agent式监控是在服务器内部采集指标CPU、内存、磁盘这些看得很清楚但业务层好不好用它看不出来。举个很实际的例子服务器一切正常但业务流程里有个第三方接口挂了导致用户下单走到最后一步就报错。这种问题Agent再强也发现不了因为它根本不访问业务接口。PLFM_RADAR的做法是站在用户的角度去探测把每一次探测当成一次真实的业务请求能通不能通、响应快不快、返回的数据对不对这些才是平台是否健康的真正信号。另外一个痛点是告警的狼来了效应。传统监控配置阈值之后经常出现半夜被无关紧要的告警吵醒的情况时间长了大家就麻木了真正严重的问题反而被忽略。PLFM_RADAR在设计之初就加入了状态抖动的抑制机制只有当异常连续出现N次或者达到某个趋势条件时才真正触发告警从机制上减少噪音。部署形态上它就是一个Python写的独立服务可以跟业务完全分开丢到一台小机器上就能跑。对中小团队来说这比维护一整套监控体系要友好得多。2. 整体架构与核心设计思路2.1 系统由哪几块拼起来整个系统我按四个模块来设计探测模块、状态汇聚模块、研判模块、通知模块。各模块各管一段尽量松耦合。探测模块负责定义怎么探测。这个不是单指发一个HTTP请求那么简单而是有一套可配置的探测规则。比如针对订单平台我可以配置一条探测规则每60秒请求一次订单查询接口带上一组测试参数判断返回的HTTP状态码、响应时间、JSON结构是否完整。针对后台管理系统可以配置一次模拟登录流程验证关键的登录链路是否正常。每条规则都是一个独立的探测任务互不影响。状态汇聚模块负责把零散的探测结果汇总起来。每一条探测任务每跑一次就产出一个结果这些结果需要按平台、按时间维度做聚合。我在这个模块里维护了一张内存中的状态表记录每个探测目标最近一段时间的探测情况包括成功率、平均响应时间、抖动幅度等指标。等需要判断平台整体健康状况的时候直接查这张状态表就行不用临时去翻历史数据。研判模块是这个系统的核心。它拿到汇聚好的状态数据之后要回答一个关键问题当前这个平台到底算正常、可疑还是异常这里面不是简单的阈值判断而是把多种信号融合起来看。比如某个接口的响应时间超过了阈值但只有一次可能只是网络抖动如果连续五次都超时那大概率是真有问题了。再比如成功率掉到了90%以下同时平均响应时间翻了倍这种组合信号比单一指标超标要严重得多。通知模块负责把研判结果推给对应的人。我接入了钉钉、企业微信、邮件三种渠道不同级别的事件走不同的渠道。警示级别的事件只发邮件重要级别的事件发钉钉群里紧急级别的才会直接打电话或发短信这里用的是阿里云的短信网关。通知策略里还加了一层值班人的逻辑白天和晚上告警的接收人可以配置成不同的人。2.2 技术栈选择的几个理由整套系统用Python 3.10写的核心依赖只有四个aiohttp做异步HTTP请求、APScheduler做定时调度、Redis做状态缓存、SQLite做持久化存储后期数据量大了可以换MySQL。为什么用Python而不是Go说实话这个体量的工具用Go有点杀鸡用牛刀Python的开发效率和灵活性更适合快速迭代。异步这块用了asyncio配合aiohttp这是关键决策。因为探测任务通常是IO密集型的等一个HTTP响应可能要几百毫秒如果串行跑100个探测任务一轮下来可能要几十秒但用异步并发的话一轮几秒钟就搞定。Redis在这个系统里承担的角色是状态表的高速缓存。原本我也考虑过纯内存存储但后来发现有个现实问题如果服务重启或者部署新版本内存里的历史状态就全部丢失了重新收集状态又需要一段时间这段时间如果平台刚好出问题雷达就是瞎的。把状态表放到Redis里即便服务重启也能立刻恢复之前的判断依据。APScheduler用来驱动探测任务的调度。支持cron表达式可以灵活定义每种探测规则的执行频率。有的接口需要高频探测比如支付回调就设30秒一次有的低频就行比如每日汇总报告接口设置每小时一次就够。2.3 数据模型与状态流转数据模型这块我花了不少心思。核心有三张表探测目标表、探测记录表、告警事件表。探测目标表描述探测什么和怎么探测关键字段包括目标ID、目标名称、所属平台、探测URL、请求方式、预期状态码、超时时间、探测间隔、启用状态。这里有一点需要注意同一个平台下可能有多个探测目标比如一个电商平台商品查询接口、下单接口、支付回调接口都要分别监控平台级别的健康状态是由这些子目标的状态聚合出来的。探测记录表记录每一次探测的原始结果这个表是持续增长的我会做定期清理只保留最近7天的数据。告警事件表记录每一次触发的告警包括事件等级、触发的探测目标、当时的指标值、恢复时间。这个表主要用于事后复盘看看告警的准确率如何哪些告警是误报。状态流转上我设计了三个状态NORMAL正常、SUSPECT可疑、FAULT故障。状态不会跳变必须经过一个确认过程。比如一个目标连续两次探测失败从NORMAL转为SUSPECT达到连续五次失败才转为FAULT。反过来从FAULT恢复到NORMAL也不是立刻完成的需要连续三次成功探测才会转回。这种滞后处理能过滤掉大部分瞬时抖动。3. 核心模块拆解与实现细节3.1 探测模块不只是发个请求探测模块是整个系统里跟我预期差距最大的部分。起初我觉得不就是发个HTTP请求然后记录结果吗但设计到细节才发现很多东西需要认真考虑。首先是探测请求的构造。不能用简单的GET请求一把梭因为很多接口需要身份认证。我在探测目标配置里加了两个可选字段请求头模板和请求体模板。通过请求头模板可以携带自定义的Header比如Authorization。请求体模板是为了应对POST类接口比如订单查询接口需要传订单号参数。模板里的变量用${}占位通过文件方式提供测试数据。这样做的好处是我可以在探测配置里定义用什么样的数据去供测试接口查询而不是让开发改接口来适配监控。其次是超时控制和重试机制。我给每个探测目标都单独配置了超时时间默认是5秒。重试策略为主守则是有节制的——最多重试一次因为重试太频繁会加剧对被测系统的压力同时也会让探测结果失去参考意义。还有一个容易被忽视的细节探测频率不能与普遍的时间边界对齐否则容易产生自激振荡。比如所有任务都在整秒或整分开始时执行如果某个接口在整点有批处理任务那么定时探测会反复撞上高延迟窗口造成周期性误报。我的解决办法是对每个目标的首轮执行时间做一个随机偏移让探测在时间轴上均匀散开。在代码层面每个探测任务都是一个异步函数内部大致流程是从目标配置里读取探针参数、构造请求、发起调用、记录耗时与状态码、解析响应体、判定结果、写Redis缓存。整个函数尽量做到无状态方便并发调度。3.2 研判模块怎样算异常怎样算故障研判模块是决定这个系统好不好用的关键。阈值设得太敏感会被告警淹没设得太迟钝又失去了预警的意义。我最终采用的是多指标加权模型。每个探测目标产生三个基础指标请求是否成功布尔值、响应耗时毫秒、数据完整性是否满足预期布尔值。研判的时候不是只看当前这一条记录而是综合最近一段时间窗口内的表现。时间窗口默认取过去5分钟内的所有探测记录。研判逻辑分几个层次如果当前探测失败且过去5分钟内失败率低于30%则标记为单次波动不产生状态变更。如果过去5分钟内成功率低于95%或者连续3次探测失败则状态从NORMAL转到SUSPECT。如果过去5分钟内成功率低于80%或者连续5次探测失败则状态升级为FAULT。除了成功率响应耗时的趋势也会被纳入研判。我做了个简单的一元线性回归斜率超过一定阈值说明响应时间在持续劣化即便当前还没超阈值也会发出预警。这个功能让我提前发现了两次慢SQL导致的问题算是意外惊喜。数据完整性校验则需要针对不同接口自定义规则。比如有的接口返回JSON里要有code字段且值为0有的接口要有data.list且长度大于0。我在配置项里加了一个assert_exprs字段用简单的表达式解析来判断这些条件是否满足。研判模块每30秒跑一次遍历Redis里的所有目标状态产出各平台的整体健康度。整体健康度分为四级绿色全部正常、黄色有可疑目标、橙色有故障目标但非核心业务、红色核心业务故障或平台整体不可用。3.3 告警通知从噪音中找到真正需要处理的事件告警通知这个环节我最大的体会是告警不是越多越好而是越准越好。首先要解决的是告警收敛。系统在同一时间段内可能对同一个目标产生大量告警不能让手机被连续轰炸。我的方案是引入告警合并窗口与静默期机制。同一目标生成告警后默认进入30分钟的静默期静默期内即使状态持续异常也不会重复推送但会在Redis里累积状态变化信息。30分钟静默期结束后如果问题仍未恢复再推送一条持续异常通知。其次是告警升级。同一个故障如果长时间未恢复告警等级会从重要自动升级到紧急从而触发更醒目的通知渠道。这个逻辑用一个简单的定时检查来实现FAULT状态持续超过15分钟后事件等级自动升一级。还有个细节是通知内容里一定要带人话。我的每条告警消息都会附上以下信息哪个平台、哪个探测目标、异常指标的具体值、持续时长、最近一次正常时间、平台负责人的联系方式。这样接收人一线拿到告警就能往下走流程不用再登录系统去翻看详情。4. 实操过程从零扎一套能跑起来的 PLFM_RADAR4.1 准备环境与配置骨架整个部署我建议直接在一台2核4G的Linux服务器上搞定资源占用很小。系统依赖就装一个Python 3.10和Redis。下面是我的环境准备命令# Ubuntu / Debian 系 sudo apt update sudo apt install -y python3.10 python3.10-venv redis-server sudo systemctl enable redis-server sudo systemctl start redis-server # 创建项目目录与虚拟环境 mkdir -p /opt/plfm_radar cd /opt/plfm_radar python3.10 -m venv venv source venv/bin/activate # 安装依赖 pip install aiohttp apscheduler redis pyyaml项目的核心配置我用YAML格式维护放在config.yaml。一开始骨架是这样redis: host: 127.0.0.1 port: 6379 db: 5 schedule: judge_interval: 30 # 研判模块执行间隔秒 state_ttl: 3600 # 状态表过期时间秒 notify: dingtalk_webhook: wecom_webhook: smtp: host: port: 465 user: password: sms: provider: aliyun access_key: access_secret: sign_name: template_code: targets: - id: order-query name: 订单查询接口 platform: 交易平台 url: https://api.example.com/order/query method: GET headers: Authorization: Bearer ${TOKEN} timeout: 5000 interval: 60 retry_once: true assert_exprs: - code 0 - len(data.list) 0 alert_level: 2 - id: cron-report-scan name: 定时报表任务探活 platform: 数据平台 url: https://api.example.com/report/health method: POST body: task: daily-summary headers: Content-Type: application/json timeout: 8000 interval: 300 assert_exprs: - status running4.2 核心代码探测器与调度器接下来是代码实现。我先写探测器的核心模块这是整个系统的地基。# modules/probe.py import asyncio import time import json import aiohttp from typing import Optional from .config import load_config class ProbeResult: __slots__ (target_id, ok, status_code, response_time_ms, detail, ts) def __init__(self, target_id, ok, status_code, response_time_ms, detail, ts): self.target_id target_id self.ok ok self.status_code status_code self.response_time_ms response_time_ms self.detail detail self.ts ts async def run_single_probe(target: dict, token: str) - ProbeResult: url target[url] method target.get(method, GET).upper() headers target.get(headers, {}) timeout_ms target.get(timeout, 5000) # 替换模板变量 headers {k: v.replace(${TOKEN}, token) for k, v in headers.items()} timeout aiohttp.ClientTimeout(totaltimeout_ms / 1000) start time.perf_counter() detail ok False status_code 0 try: async with aiohttp.ClientSession(timeouttimeout) as session: if method GET: resp await session.get(url, headersheaders) elif method POST: resp await session.post( url, headersheaders, datajson.dumps(target.get(body, {})), sslFalse ) else: resp await session.request(method, url, headersheaders) status_code resp.status text await resp.text() response_time_ms (time.perf_counter() - start) * 1000 if resp.status 400: detail fHTTP {resp.status} else: ok check_assert_exprs(target.get(assert_exprs, []), text) if not ok: detail 断言失败 except asyncio.TimeoutError: response_time_ms (time.perf_counter() - start) * 1000 detail f超时{timeout_ms}ms except Exception as e: response_time_ms (time.perf_counter() - start) * 1000 detail f异常: {e.__class__.__name__}: {e} return ProbeResult( target_idtarget[id], okok and status_code 400, status_codestatus_code, response_time_msresponse_time_ms, detaildetail, tstime.time() ) def check_assert_exprs(exprs: list[str], response_text: str) - bool: if not exprs: return True try: data json.loads(response_text) except json.JSONDecodeError: return False for expr in exprs: if not eval_expr(data, expr): return False return True4.3 研判模块综合信号的状态判定探测器只负责采集原始数据真正拍板的是研判模块。这部分的思路我之前讲过核心是窗口滑动统计 状态机流转。我直接贴出关键的状态机逻辑# modules/judge.py import time import redis import json class PlatformJudge: # 状态流转阈值 THRESHOLD_SUSPECT 0.95 # 成功率低于95%进入可疑 THRESHOLD_FAULT 0.80 # 成功率低于80%进入故障 CONSECUTIVE_SUSPECT 3 # 连续3次失败进入可疑 CONSECUTIVE_FAULT 5 # 连续5次失败进入故障 RECOVER_OK 3 # 连续3次成功才恢复 def __init__(self, redis_conn, targets_config): self.r redis_conn self.targets targets_config def _recent_records(self, target_id: str, window_seconds: int 300): 从Redis的近期记录中获取滑动窗口内的探测结果。 key fplfm:records:{target_id} end time.time() start end - window_seconds records self.r.zrangebyscore(key, start, end) return [json.loads(r) for r in records] def evaluate(self, target_id: str): 对单个探测目标执行状态判定。 target_config next( (t for t in self.targets if t[id] target_id), None ) if not target_config: return records self._recent_records(target_id) if not records: return total len(records) ok_count sum(1 for r in records if r[ok]) success_rate ok_count / total cur_state_key fplfm:state:{target_id} cur_state self.r.get(cur_state_key) if cur_state is None: cur_state bNORMAL cur_state cur_state.decode() # 计算连续失败次数 consecutive_fail 0 for r in reversed(records): if r[ok]: break consecutive_fail 1 # 计算连续成功次数 consecutive_ok 0 for r in reversed(records): if not r[ok]: break consecutive_ok 1 new_state cur_state if cur_state NORMAL: if success_rate self.THRESHOLD_FAULT or consecutive_fail self.CONSECUTIVE_FAULT: new_state FAULT elif success_rate self.THRESHOLD_SUSPECT or consecutive_fail self.CONSECUTIVE_SUSPECT: new_state SUSPECT elif cur_state SUSPECT: if success_rate self.THRESHOLD_FAULT or consecutive_fail self.CONSECUTIVE_FAULT: new_state FAULT elif consecutive_ok self.RECOVER_OK: new_state NORMAL elif cur_state FAULT: if consecutive_ok self.RECOVER_OK: new_state NORMAL # 状态变化时写入新状态并触发告警 if new_state ! cur_state: self.r.set(cur_state_key, new_state) if new_state in (SUSPECT, FAULT): self._trigger_event(target_id, target_config, cur_state, new_state, records, success_rate) def _trigger_event(self, target_id, target_config, old_state, new_state, records, success_rate): event { target_id: target_id, target_name: target_config[name], platform: target_config[platform], old_state: old_state, new_state: new_state, success_rate: round(success_rate, 3), ts: time.time(), } self.r.lpush(plfm:events, json.dumps(event))为什么这里用Redis的sorted set存储探测记录因为按时间范围取记录这个场景太常见了。用zrangebyscore直接按时间戳取性能很好还顺带按时间排序了。每条记录我会设置一个全局过期时间TTLRedis的zremrangebyscore定期清理就行不需要额外写清理逻辑。4.4 告警与通知让事件真正触达责任人告警模块我设计成基于事件队列的消费模式。研判模块产生的所有事件先放进Redis里的plfm:events列表通知模块作为独立消费者从队列里拉取事件。# modules/notifier.py import time import hashlib import requests import smtplib from email.mime.text import MIMEText class Notifier: SILENCE_SECONDS 1800 # 静默期30分钟 ESCALATE_SECONDS 900 # 15分钟未恢复则升级 def __init__(self, cfg): self.cfg cfg self.sent_cache_key plfm:notify:sent def _dedup_key(self, event_id): return fplfm:dedup:{event_id} def send(self, event: dict, redis_conn): target_id event[target_id] state_key fplfm:state:{target_id} cur_state redis_conn.get(state_key).decode() # 故障持续事件升级 if cur_state FAULT: first_ts float(redis_conn.get( fplfm:fault_since:{target_id}) or time.time()) duration time.time() - first_ts if duration self.ESCALATE_SECONDS: event[alert_level] 1 # 紧急 dedup_key self._dedup_key( f{target_id}:{event[new_state]}:{event.get(alert_level, 2)} ) # 静默期去重 if redis_conn.set(dedup_key, 1, exself.SILENCE_SECONDS, nxTrue): self._do_send(event) def _do_send(self, event): level event.get(alert_level, 2) if level 1: self._send_sms(event) self._send_dingtalk(event) elif level 2: self._send_dingtalk(event) else: self._send_mail(event)需要说明的是_send_sms、_send_dingtalk这些函数内部实现就是调用各类开放API核心逻辑是把事件信息格式化成对应渠道的消息模版。短信这块我用的是阿里云的短信服务模板内容要提前在平台审核我平时审核通过后的模板里就只保留了平台名、目标名、异常描述三个占位符。4.5 主程序与部署脚本所有模块都写完之后主程序反而很简单——就是把调度器拉起来把各类任务注册进去。# main.py import asyncio import yaml import redis from apscheduler.schedulers.asyncio import AsyncIOScheduler from modules.probe import run_single_probe from modules.judge import PlatformJudge from modules.notifier import Notifier async def main(): with open(config.yaml, r, encodingutf-8) as f: cfg yaml.safe_load(f) r redis.Redis(hostcfg[redis][host], portcfg[redis][port], dbcfg[redis][db]) scheduler AsyncIOScheduler() judge PlatformJudge(r, cfg[targets]) notifier Notifier(cfg[notify]) # 为每个探测目标注册定时任务 for target in cfg[targets]: interval target.get(interval, 30) scheduler.add_job( probe_and_record, interval, secondsinterval, args(target, r, judge, notifier), idtarget[id], replace_existingTrue, misfire_grace_time10 ) # 注册全局研判任务 scheduler.add_job( global_judge_loop, interval, secondscfg[schedule][judge_interval], args(judge, r), idglobal-judge, replace_existingTrue ) scheduler.start() print([PLFM_RADAR] started.) # 保活 while True: await asyncio.sleep(10) def probe_and_record(target, r, judge, notifier): 执行探测并记录结果。 token load_credential(target) # 从本地安全文件读取token result asyncio.run(run_single_probe(target, token)) record { target_id: result.target_id, ok: result.ok, status_code: result.status_code, response_time_ms: result.response_time_ms, detail: result.detail, ts: result.ts } r.zadd(fplfm:records:{result.target_id}, json.dumps(record), result.ts) async def global_judge_loop(judge, r): 全局定时执行状态研判。 for target in judge.targets: judge.evaluate(target[id]) # 消费事件触发通知 notifier Notifier(cfg[notify]) events r.lrange(plfm:events, 0, -1) for e in events: event json.loads(e) notifier.send(event, r) r.lrem(plfm:events, 1, e) if __name__ __main__: asyncio.run(main())部署的时候我用systemd做进程守护这样重启服务器后服务会自动起来。配一个plfm-radar.service文件扔到/etc/systemd/system/下面就行[Unit] DescriptionPLFM_RADAR Platform Monitoring Afternetwork.target redis-server.service [Service] WorkingDirectory/opt/plfm_radar ExecStart/opt/plfm_radar/venv/bin/python /opt/plfm_radar/main.py Restartalways RestartSec5 [Install] WantedBymulti-user.target这套流程完整跑起来之后从创建配置到产出第一条告警信息熟练的话一个人半小时就能搞定。5. 常见问题与排查技巧实录5.1 告警风暴一次Redis连接风暴差点压垮自己上线第一周我遇到一个特别打脸的问题系统刚部署好的第二天凌晨告警群里突然涌进来上百条消息全是交易平台-订单查询接口-故障。我赶紧爬起来查发现订单平台本身完全正常——问题出在雷达自己身上。排查过程是这样的由于探测任务都用异步并发执行100个探测目标同时发起探测请求而这100个请求的响应结果要同时往Redis里写。当时Redis的maxclients配置是默认值一下子连接数被打满后续的写请求全部超时。而探测结果写入Redis一旦超时探测模块就当成异常处理于是100个目标全部被判成故障。这个问题的教训有两个层面。技术上我在写入Redis之前加了一层本地缓冲先写内存队列再由一个后台任务批量写入Redis这样Redis的写入频率就降下来了。另外我把Redis的maxclients调到了512。思路上我意识到监控系统自身也可能成为故障源所以给雷达自身的关键路径Redis写入、调度器心跳单独做了日志和健康检查不能让雷达带病误报。5.2 时间漂移导致误判还有一个坑是服务器时间不同步引发的。我们的几台部署环境里有一台的系统时间比真实时间慢了大约两分钟。PLFM_RADAR判断故障时用的是滑动时间窗口这个窗口以本机时间为准。那台时间慢的服务器上探测请求发出去用的是真实的服务端时间接收但记录时间戳用的是本机时间就导致了一批探测记录的时序错乱。具体表现是本机认为最近5分钟内有8条探测记录但其实这些记录是7分钟之前的而真正最近5分钟内没有新记录状态一直停留在上一次的历史状态上。这会导致响应时间趋势判断失效。解决办法分两步第一所有部署雷达的服务器都加上NTP时间同步第二在时间戳的使用上统一用第一次收到响应时的时间而不是落库时的时间。这样即使时间有小幅漂移也不至于让整个窗口的数据错位。5.3 探测请求污染生产数据这个问题我在设计时其实想到了但真出问题的时候还是捅了个篓子。我在订单查询接口的测试数据里用了一笔真实存在的订单号结果这台订单在被做退款操作后状态变化了探测请求命中该订单已退款的返回分支断言表达式status paid就不再满足了持续报故障。查了半天才意识到探测数据不能从生产环境里随便拿必须用专门的测试账号和测试数据。我吃一堑长一智把探测目标的数据源统一改成了测试库并给所有探测请求的Header里加了一个自定义标记字段业务日志里可以辨别哪些流量来自雷达探测。5.4 HTTPS证书带来的检测盲区项目跑通一段时间后我偶然发现某个平台的探测请求里SSL证书校验警告被忽略了。由于我用了sslFalse跳过证书校验等于把证书过期这类问题直接过滤掉了。对一个平台雷达来说这其实是个比较大的盲区因为证书过期恰恰是线上高发的故障类型。校正方案是增加一个SSL证书有效期检查任务不依赖业务探测请求而是单独对每个域名的证书剩余天数做采集剩余天数少于7天就告警。这个功能我后来做成了独立的探测类型配置也很简单- id: cert-check-www name: 主站证书有效期 platform: 交易平台 type: cert domain: www.example.com warn_days: 15 alert_days: 75.5 快查速记最后整理一份我在整个调优过程中积累的最有用的排查清单几乎每次告警都能用到。现象最可能原因快速排查手段所有目标同时报故障雷达自身漂移Redis连接满、网络不通、NTP偏差先看雷达自身日志再查Redis连接数单个目标持续故障但业务正常探测数据不可用、断言太严格、测试数据状态变化手动跑一次探测直接检查返回体告警重复推送静默期太短、去重键设计不合理检查Redis去重键是否存在响应时间趋势告警频繁存在定时批处理任务撞上探测窗口给该目标添加随机延迟错峰探测告警延迟明显judge_interval设太长或者Redis事件队列堆积调低全局研判间隔检查队列长度6. 复盘这套系统还能怎么扩展PLFM_RADAR目前已经在我们这边稳定跑了几个月最大价值确实不在发现故障——故障总有其他手段能发现——而在于一种让它过去的安全感。以前每次发完版本我都得盯一会儿监控面板现在雷达会自动帮你盯着而且是从用户视角盯着。我自己在实际操作中有一个很深的体会这套系统的核心其实不在代码而在探测目标和断言规则的定义。代码只是骨架真正反映你对业务理解的是那一条条探测规则——哪些接口最关键、什么返回结构代表正常、响应时间多久开始需要重视这些判断直接决定了雷达的价值。建议每一个新接手的同学在熟悉业务的时候把重点放在梳理探测目标清单上而不是急着调代码。这个项目目前有一些我还没完全做完的事情。比如多地域探测现在雷达部署在单台机器上看到的网络链路是单一的未来如果要覆盖更多场景可以考虑在不同网络环境部署多台探测节点把网络质量因素单独抽出来建模。另外告警的智能降噪目前还是基于规则引擎如果统计波动大可以试试引入更多基础性的方法比如用历史同期数据进行基线对比从根本上识别出与历史常态不一样的风险信号。如果你也想在自己的团队里搭一个类似的东西我建议别一开始就求大求全先挑一个最重要的接口、一条最核心的链路把探测、研判、通知三个环节跑通。等真正跑顺了再逐步扩大覆盖面。这套系统的价值是随着你对业务的理解而逐步放大的。最后再分享一个我在踩坑之后养成的习惯每次做完一个平台的接入我都会顺手写一条故障演练计划——手动停掉被测平台的某个依赖服务看雷达要多久能发现问题、告警信息够不够让排障的人快速定位。这比任何文档都有用因为它直接考验的是整个链路而不是某个环节。建议你也可以这么试一次准能发现不少意想不到的问题。
返回列表