如何排查Golang GraphQL订阅API仅返回第一条数据的问题?
问题原因及解决方案
核心原因
你的订阅连接在发送第一条消息后被提前终止,触发ctx.Done()分支,主要诱因有两个:
- GCP负载均衡的空闲超时机制:GCP HTTP(S)负载均衡默认空闲连接超时为30秒,若消息发送无间隔、传输层未维持长连接活跃,负载均衡会主动断开连接,进而取消请求上下文。
- SSE传输的兼容性问题:若gqlgen默认使用Server-Sent Events(SSE)作为订阅传输,GCP部分环境对SSE长连接的支持有限,易被中途断开。
解决方案
1. 添加消息发送间隔,维持连接活跃
在循环中加入短暂延迟,模拟真实场景的消息节奏,避免连接被判定为空闲:
func (r *subscriptionResolver) CreateChatWithBot(ctx context.Context, req model.CreateChatWithBotRequest) (<-chan *model.CreateChatWithBotResults, error) { ch := make(chan *model.CreateChatWithBotResults) testString := []string{"This", "part", "is", "under", "construction", "...", " Be", "patient", "please!"} go func() { defer close(ch) for _, v := range testString { select { case <-ctx.Done(): return case ch <- &model.CreateChatWithBotResults{Response: pointer.Pointer(v)}: // 添加1秒延迟,维持连接活跃 time.Sleep(1 * time.Second) } } }() return ch, nil }
2. 切换为WebSocket传输协议
相比SSE,WebSocket在GCP环境下的长连接稳定性更强。修改gqlgen服务配置启用WebSocket:
func main() { c := config.DefaultConfig() // 其他配置代码... srv := handler.NewDefaultServer(generated.NewExecutableSchema(c)) // 启用WebSocket订阅支持 srv.AddTransport(transport.Websocket{ Upgrader: websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { // 根据业务需求配置跨域规则 return true }, }, }) // 启动服务代码... }
之后在Postman中使用WebSocket方式调用订阅API,替代原HTTP请求方式。
3. 调整GCP负载均衡超时设置
若前两种方法无效,可在GCP控制台延长负载均衡的空闲超时:
- 进入负载均衡页面,找到目标实例
- 编辑后端服务,将“空闲超时”参数调整为更长时间(最长支持86400秒)
验证顺序
- 先测试添加消息延迟的方案,确认是否能接收全部消息
- 若问题依旧,切换到WebSocket传输协议
- 最后考虑调整GCP负载均衡的超时配置
内容的提问来源于stack exchange,提问作者mrasoolmirza
相关产品推荐
相关产品推荐

