Golang消息通知系统需分层清晰、职责分明、易于扩展:定义统一Notifier接口解耦通道,事件驱动+规则引擎匹配模板,模板支持变量渲染与热加载,失败任务延迟重试并暴露Prometheus指标。

用 Golang 开发消息通知系统,核心不在堆砌功能,而在分层清晰、职责分明、易于扩展。模块化设计不是为了炫技,而是让通知逻辑可插拔、可配置、可灰度、可监控。
通知通道要解耦,别写死在业务里
邮件、短信、企业微信、钉钉、站内信、WebSocket 推送——这些不该混在用户注册或订单完成的主流程中。正确做法是定义统一的通知接口:
- 每个通道实现 Notifier 接口(如
Send(ctx, recipient, content)) - 业务侧只调用
notificationService.Notify(event, payload),不关心走哪条路 - 通道启用状态、限流规则、重试策略全部通过配置中心或数据库动态控制
事件驱动 + 规则引擎,避免硬编码判断
“用户下单成功就发短信”这种逻辑如果写在 if 分支里,很快就会变成维护噩梦。建议:
- 将业务动作抽象为标准事件(如
order.created、user.registered) - 用轻量规则引擎(如基于 map[string][]Rule 的内存规则表)匹配事件与通知模板
- 每条规则含:触发条件(可为空)、目标通道、模板 ID、是否异步、是否跳过测试环境
模板管理必须支持变量渲染和多语言
一条通知内容不能靠字符串拼接。应设计模板结构体:
立即学习“go语言免费学习笔记(深入)”;
- 模板按渠道 + 语言 + 场景唯一标识(如
sms_zh_CN_order_success) - 内容支持 Go
text/template语法,自动注入.User.Name、.Order.Amount等上下文字段 - 模板变更无需重启服务,热加载或从 DB/Redis 拉取最新版本
失败处理和可观测性不能省
通知失败很常见,但日志打满、重试无节制、没人知道哪条没发出去,才是真问题:
- 每次发送记录 trace ID、事件类型、目标 ID、通道、状态、耗时、错误摘要
- 失败任务进入延迟队列(可用 Redis ZSET 或简单 channel+定时器),支持最多 3 次指数退避重试
- 暴露 Prometheus 指标:成功数、失败数、平均延迟、各通道积压量
基本上就这些。模块化不是把代码切开就完事,而是让每一环都能独立演进、单独压测、按需替换。比如某天要接入飞书机器人,只需新增一个 FeishuNotifier 实现,改下配置,其他全不动。










