Kratos本身不内置长轮询支持,但可在HTTP handler中用net/http实现:需禁用超时、正确使用context和事件channel;http.TimeoutHandler会强制返回504使长轮询退化为短轮询,且默认Read/WriteTimeout必须设为0,否则连接被中间件切断。

Kratos 本身不内置长轮询支持,但完全可以用标准 net/http 方式在 Kratos 的 HTTP handler 中实现,关键在于禁用超时、正确使用 context 和事件 channel。
为什么不能直接用 Kratos 的 http.TimeoutHandler
因为 http.TimeoutHandler 在超时后会强制关闭连接并返回 504,这会让长轮询退化成短轮询——客户端刚收到空响应,立刻发下一次请求,完全失去“有数据立刻返回”的意义。Kratos 默认的 HTTP server 配置(如 ReadTimeout / WriteTimeout)也必须设为 0,否则 Nginx 或 Go 自身会在中间切断连接。
常见错误现象:
- 前端看到大量 504 或连接被 reset,日志里反复出现
context deadline exceeded - 明明服务端已推送数据,客户端却卡住直到超时才收到响应
- 并发一高,goroutine 数暴涨且不回收,内存持续增长
在 Kratos 的 HTTP handler 里怎么写长轮询逻辑
Kratos 的 HTTP handler 本质还是 http.HandlerFunc,你可以在 internal/server/http.go 或自定义路由中直接注册一个长轮询 endpoint,比如 /notify/longpoll。核心是用 select 监听三类信号:
-
req.Context().Done():客户端断开或主动取消(必须检查) - 自定义事件 channel(如从消息总线、
sync.Map或业务层推送来的chan interface{}) -
time.After(timeout):防止单次等待无限期挂起(建议 30–60 秒)
示例片段(非完整服务,仅展示关键控制流):
select
- 每次向
http.ResponseWriter写数据前,加if ctx.Err() != nil { return } - 不要把
*http.ResponseWriter保存到结构体或全局变量中——它不是线程安全的,且生命周期只限当前 handler - 事件推送侧用
select { case ch 非阻塞发送,防止因接收方未 ready 卡住生产者 - 用
sync.Map管理活跃连接对应的 channel,key 建议用r.RemoteAddr + timestamp避免冲突
真正难的不是写通逻辑,而是压测时暴露的连接泄漏、channel 积压、Nginx 代理超时配置遗漏——这些细节往往在开发阶段完全看不出来,只有真实用户量上来才会爆发。











