告警通知扩展到钉钉和短信:多通道路由、Token 缓存与失败隔离
导读
把原有 PC 站内通知扩展到钉钉和短信,表面上只是根据类型多调用两个接口。真正落地时却牵出四个问题:双通道重复查询用户、钉钉 Token 每次发送都重新获取、外部通道失败可能拖慢告警事务,以及“代码接入”很容易被误写成“线上已经打通”。
这篇记录一次多通道通知链路的 review:如何把共享前缀提到路由层、用进程内缓存控制 Token 请求、让各通道互不拖累,并诚实区分钉钉实测成功与短信外部环境仍阻塞的边界。
多通道不是多写两个 if
通知配置支持四种组合:仅 PC、钉钉、短信、钉钉与短信。运行时还要同时考虑总开关、接收人、用户绑定信息以及每个通道的可用性。
如果直接在两个通道方法里各自完成“解析接收人 → 查用户 → 过滤字段 → 发送”,双通道模式就会对同一批用户查询两次。告警量小时不明显,告警风暴下它会放大 RPC、数据库和事务占用。
review 后把执行链路收敛成:
1 | flowchart LR |
PC WebSocket 被放在外部查询之前。即使用户服务、短信平台或钉钉代理异常,浏览器里的基础通知仍然可以先到达。
共享前缀只做一次
重构前,钉钉和短信方法都包含相同的三步:拆分接收人 ID、远程批量查询用户、校验查询结果。双通道模式自然会执行两遍。
重构后,路由层只查询一次,再把同一个用户列表交给通道:
1 | List<User> users = loadRecipients(receiverUserIds); |
通道方法只关心自己的数据:短信提取和校验手机号,钉钉过滤未绑定用户并组装卡片。测试用“批量用户查询只调用一次”锁住这个约束,避免未来维护时又把查询塞回各通道。
Token 缓存:小改动,少一半外部请求
最初实现每发一条钉钉消息都会先请求一次 access token,再请求一次异步发送接口。Token 本身有较长有效期,这种写法不仅多一次 HTTP,还可能触发平台限流。
最终使用进程内缓存,并在过期前五分钟主动刷新:
1 | private volatile String cachedToken; |
这里的双重检查有三个目的:命中缓存时不加锁;并发刷新时只让一个线程请求;提前刷新避免拿到 Token 后刚发送就过期。测试覆盖了“有效期内只请求一次”和“过期后重新请求”。
没有一开始就引入 Redis,是因为 Token 只供单个服务进程使用,本地缓存已经满足当前规模。真正需要多实例共享或精确失效时,再升级集中式缓存更合适。
失败隔离优先于“全部成功”
告警入库是主链路,消息通知是增强能力。钉钉或短信异常不能让告警事务回滚,也不能阻止其他通道继续执行。
最终约束是:
- PC 通道先执行,不依赖外部用户映射;
- 钉钉和短信各自捕获异常,单个通道失败不影响另一个;
- 未绑定钉钉账号、手机号为空或格式无效的用户跳过并记录日志;
- 手动告警与真实告警复用同一个通知函数,避免出现两套行为。
目前外部调用仍位于告警事务中。异常虽然不会导致回滚,但慢请求仍可能占住数据库连接。这是已确认的技术债,而不是“catch 了异常就没问题”。下一步应在事务提交后异步分发,或者引入可靠事件;本次 review 没有扩大改造范围。
手动触发是最有价值的测试缝
真实告警依赖摄像头、算法和事件上报,拿它做通知联调成本很高。系统已经有手动生成告警的入口,只要它加载完整规则并复用真实的通知方法,就可以在没有真实设备事件时验证下半条链路。
推荐分段验证:
- 先直测钉钉发送服务,确认 Token、应用配置和 ActionCard;
- 再创建一条只开启单个通道的临时规则;
- 用手动告警触发业务服务到发送服务的完整链路;
- 最后再用真实设备告警做端到端验收;
- 测试完成后清理临时规则。
分段验证能够把“规则没命中”“用户没绑定”“平台凭据错误”“外部网络不通”拆开,不必每次都从真实设备重放整条链路。
测试矩阵与真实验证边界
自动化测试覆盖了这些关键分支:
- Token 缓存命中与过期刷新;
- 双通道只查询一次接收人;
- 加密和明文手机号的兼容处理;
- 无效手机号和未绑定钉钉用户的过滤;
- 任一外部通道异常时,PC 通知仍然执行;
- 手动告警能够加载完整通知配置。
截至本次记录,钉钉发送服务已经真实获取 Token、成功提交 ActionCard,测试人员也收到了卡片并验证了详情跳转。业务告警到钉钉的完整链路仍需要在新版服务部署后做最终验收。
短信则只完成代码侧接入,线上初始化仍被外部数据库账号授权或白名单阻塞。日志中的数据库拒绝访问说明问题位于短信平台环境,不代表通知业务代码已经验证成功,也不应该在发布说明里写成“短信已上线”。
小结
这次 review 最终沉淀了五条可复用经验:
- 多通道路由先抽共享前缀,用户和配置只查一次;
- 有明确有效期的外部 Token,先用最小的进程内缓存解决重复请求;
- 基础通道优先,外部通道彼此隔离,增强能力不能拖垮主链路;
- 为复杂链路保留手动触发入口,分段验证比依赖真实设备高效;
- “代码已接入”“单通道已实测”“完整链路已上线”是三个不同状态,工程记录必须明确区分。
多通道通知真正难的从来不是多调用两个接口,而是让每个通道在成功时有证据、失败时有边界,并且不把故障扩散回核心业务。