流式调用必须每次send/recv后检查ctx.err()并显式处理错误,否则会导致goroutine阻塞、连接泄漏或内存增长;需串行调用send/recv、发送完调用closesend()、禁用并发send。

流式调用必须检查 ctx.Err(),否则会卡死或泄漏
服务端流(如日志推送)或双向流中,客户端随时可能断开、超时或取消请求。gRPC 不会自动中断正在执行的 stream.Send() 循环,context.Context 是唯一可靠的通知机制。忽略它会导致 goroutine 永久阻塞、连接不释放、内存持续增长。
实操建议:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 每个流式 handler 的主循环开头或每次
stream.Send()前,都应显式判断if err := stream.Context().Err(); err != nil { return err } - 不要依赖
io.EOF作为唯一退出条件——那是客户端关闭连接时才触发,而 context 取消(如超时、主动 cancel)不会产生io.EOF - 若需在发送前做背压控制,可用
stream.SetHeader()或stream.SendMsg()配合ctx.Done()select 判断
stream.Recv() 和 stream.Send() 不能并发调用,需串行或加锁
gRPC 的流对象(如 ChatService_ChatServer)不是并发安全的。多个 goroutine 同时调用 Recv() 或 Send() 会引发 panic 或数据错乱,尤其在双向流中容易误以为“收发独立就可并行”。
实操建议:
- 接收逻辑统一放在一个 goroutine 中循环
Recv(),把消息转发到 channel 或业务队列 - 发送逻辑由另一个 goroutine 或主线程串行调用
Send(),避免直接在Recv()循环里混写Send() - 若必须动态响应(如收到指令立刻回包),用
select+ channel 协调,而非裸调Send() - 切勿在同一个 stream 上起多个 goroutine 调用
Send()—— 即使加了 mutex,也违背 gRPC 流语义,易触发transport: failed to write a frame
客户端流式调用要主动关闭发送端,否则服务端一直等
客户端流(rpc Upload(stream Chunk) returns (Status))要求客户端显式结束发送,否则服务端的 Recv() 会永远阻塞在最后一次读取,返回 io.EOF 的时机完全取决于客户端是否调用 CloseSend()。
实操建议:
- 发送完所有数据后,必须调用
stream.CloseSend();漏掉这一步,服务端无法进入“收尾逻辑”,也无法返回最终响应 - 若发送中途出错(如网络中断),
CloseSend()仍应被调用,否则资源无法清理 - 使用
defer stream.CloseSend()容易掩盖错误——应在明确发送完成后再调用,而不是 defer 到函数末尾 - 配合
context.WithTimeout控制整体生命周期,防止CloseSend()自身卡住(如底层连接已断但未感知)
流式错误传播不可靠,status.FromError() 必须在每次 Send()/Recv() 后检查
流式调用中,错误不会像 unary RPC 那样集中返回一次。Send() 失败可能是连接断开、缓冲区满或对方已 cancel;Recv() 失败可能是 EOF、超时或协议错误。这些错误必须当场处理,延迟捕获会导致状态错乱或静默失败。
实操建议:
- 每次
stream.Send(x)后立即判断if err != nil { return err },不要攒多个再发、再统一 check - 每次
stream.Recv()后区分三种情况:err == io.EOF(正常结束)、err != nil && err != io.EOF(异常)、err == nil(继续) - 用
status.FromError(err)解析具体错误码(如codes.DeadlineExceeded、codes.Canceled),而非仅打印err.Error() - 服务端流中,若
Send()返回transport: error writing response: broken pipe,说明客户端已断开,应立刻 return,不要重试
Send()、Recv()、CloseSend() 和 ctx.Err() 的即时响应——少一次判断,就多一分泄漏或卡死风险。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










