mercure 是基于 http/2 server push 和 sse 的独立发布-订阅中间件,微服务仅需通过带 jwt 认证的 http post 向其推送更新,前端用 eventsource 订阅;它解决了原生 sse 的保活、广播、重连、认证及生态兼容难题。

Mercure 不是 SSE 的替代品,而是基于 HTTP/2 Server Push 和 SSE 协议构建的现代发布-订阅分发系统;它本身不直接“接入”微服务作为 SSE 网关,而是作为独立的、可部署的中间服务(类似消息代理),微服务通过 HTTP POST 向其推送更新,前端通过标准 EventSource 订阅——Golang 微服务只需扮演“发布者”,无需实现 SSE 流逻辑。
为什么不用自己写 SSE 而选 Mercure
Mercure 解决了纯 SSE 实现中最难缠的几个问题:连接保活超时、多实例广播一致性、客户端重连状态丢失、JWT 认证集成、以及与现有微服务生态(如 Redis、Kubernetes)的天然兼容性。你自己用 http.ResponseWriter + Flush() 写 SSE,容易在高并发下卡住 goroutine、漏 flush、或被反向代理(如 Nginx)静默断连;Mercure 用 Caddy 或专用服务器处理这些底层细节,Golang 侧只管发 HTTP 请求。
微服务向 Mercure 发送更新的正确姿势
关键不是“怎么连 Mercure”,而是“怎么构造合法的 POST 请求”。Mercure 要求:
-
POST /.well-known/mercure(路径固定,不可改) -
Content-Type: application/x-www-form-urlencoded或application/json(推荐后者) - 必须带
Authorization: Bearer <jwt></jwt>,且该 JWT 需含publish权限(由 Mercure 的JWT_KEY签发) - body 至少含
topic(URI 格式,如https://api.example.com/orders/123)和data(任意 JSON 字符串)
示例代码片段(使用 net/http):
reqBody, _ := json.Marshal(map[string]interface{}{
"topic": "https://api.example.com/notifications",
"data": `{"id":"notif_abc","type":"new_message","content":"Hello"}`,
})
req, _ := http.NewRequest("POST", "http://mercure:8080/.well-known/mercure", bytes.NewReader(reqBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+generatePublishToken()) // 自行实现 JWT 签发
resp, _ := http.DefaultClient.Do(req)
⚠️ 容易踩的坑:topic 必须是绝对 URI;data 是字符串,不是对象(即使内容是 JSON,也要先 json.Marshal 成字符串再塞进去);JWT 过期时间建议设为 5 分钟以内,避免泄露后长期有效。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
Gin 或其他框架中封装发布逻辑
不要在每个 handler 里重复写 HTTP client 调用。建议抽成一个可注入的服务:
- 定义接口:
type MercurePublisher interface { Publish(topic string, data interface{}) error } - 实现时复用
http.Client(设置Timeout,避免阻塞) - 对
data统一做json.Marshal→string转换,防止调用方传错类型 - 记录失败日志并返回 error,**不要静默忽略 Mercure 不可达**(它挂了,你的通知就丢了)
如果你用 Gin,可在中间件或 service 层注入该 publisher,比如订单创建后:publisher.Publish("https://api.example.com/orders/"+order.ID, order) —— 注意这里 order 会自动被序列化为 JSON 字符串。
前端订阅与 Mercure 的 CORS/Origin 控制
前端用 new EventSource("/.well-known/mercure?topic=https://api.example.com/notifications") 即可,但 Mercure 默认只允许同源请求。若前端域名与 Mercure 不同(常见于分离部署),需配置 Mercure 的 ALLOWED_ORIGINS 环境变量(如 ALLOWED_ORIGINS="https://app.example.com")。Golang 微服务本身不参与这个环节,但你要确保部署时环境变量已设好——否则前端会看到 Network Error 且无详细提示。
最常被忽略的是:Mercure 的 JWT 认证和 topic 权限模型是解耦的。即使你发出了正确的 publish 请求,如果客户端订阅时没带 token 或 token 里没声明 subscribe 对应 topic 的权限,事件照样收不到。调试时先用 curl 模拟订阅,确认 Mercure 日志里有没有 “authorized” 字样,再查 Golang 侧是否真发出了请求。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










