Webman需自定义HTTP接口接收AlertManager/Zabbix Webhook并转发至钉钉/飞书,关键点包括路由配置、JSON解析、IP白名单、payload映射、签名验证、失败重试、协程并发控制及告警恢复处理。

Webman 本身不内置告警转发能力,必须靠自定义 HTTP 接口接收 AlertManager 或 Zabbix 等上游系统的 Webhook 请求,再主动调用钉钉/飞书机器人 API。直接在 Webman 里写个 AlertController 就能跑通,但容易忽略签名验证、模板拼接和错误重试这些关键点。
Webman 接收 AlertManager Webhook 的路由配置
AlertManager 默认 POST JSON 到你暴露的地址,比如 /webhook/alert。Webman 要确保该路由启用 JSON 解析,并允许跨域(调试时)或限制来源 IP(生产时)。
- 在
config/route.php中注册路由:Route::post('/webhook/alert', [AlertController::class, 'handle']) - 确认
app/Http/Middleware/JsonMiddleware.php已启用,否则$request->post()拿不到原始 JSON 数据 - 生产环境建议加一层 IP 白名单中间件,只允许 AlertManager 所在服务器 IP 访问,避免被恶意 POST
解析 AlertManager payload 并映射到钉钉/飞书格式
AlertManager 发来的 JSON 结构固定,但钉钉用 markdown、飞书用 interactive card,字段名和嵌套层级完全不同。硬编码拼字符串极易出错,推荐用数组映射 + 条件判断。
- 钉钉
msgtype=markdown要求字段是text.content,且换行用\n;飞书msg_type=interactive必须带card.header.title.content和card.elements数组 -
labels.severity可能是critical或warning,需映射为钉钉的### ? 严重告警、飞书的template=red - 飞书卡片中时间字段要转成 ISO8601 格式(
startsAt是 RFC3339),钉钉 markdown 里直接用new Date().toLocaleString()不可靠,应从 payload 提取并格式化
签名验证与失败重试逻辑不能省
钉钉支持加签(timestamp + sign),飞书支持 sign(HMAC-SHA256),没校验就直接处理,等于把机器人入口敞开。
- 钉钉校验:从请求 header 拿
timestamp和sign,用hash_hmac('sha256', $timestamp."\n".$secret, $secret)对比 - 飞书校验:header 中
X-Lark-Signature是 base64 编码的 HMAC-SHA256 值,需对原始 body + timestamp + nonce 拼接后计算 - HTTP 请求失败(如网络超时、429 频控)必须记录日志并返回非 200 状态码,否则 AlertManager 会反复重试,可能触发限流封禁
Webman 下发消息时的并发与超时控制
单次告警可能含多个 alerts,若循环调用钉钉/飞书 API,没设超时或并发限制,容易卡住整个进程。
- 用
Co\http\Client(协程客户端)代替file_get_contents或cURL,避免阻塞事件循环 - 设置连接超时 ≤ 3s、读取超时 ≤ 5s,钉钉官方 SLA 是 3s 内响应,飞书建议 ≤ 10s
- 如果同时要发钉钉+飞书,别用串行
yield $dingtalk->send(); yield $lark->send();,改用Co\run启多个协程并行,但总数限制在 5 以内
最常被跳过的其实是告警恢复(status: resolved)的处理逻辑,以及飞书卡片里按钮点击后的回调验证——这两处没做,等于只实现了半条链路。


















