gRPC支持流式通信,因net/rpc仅支持同步请求响应,而gRPC基于HTTP/2提供客户端流、服务端流和双向流,适用于实时推送等场景,需通过proto文件定义stream并生成代码实现,配合goroutine管理读写。

Go 语言的 net/rpc 包本身不支持流式通信(streaming),它只提供“请求-响应”式的同步调用。若需实现真正的流式 RPC(如服务端持续推送数据、客户端边发边收、双向实时传输),应使用 gRPC —— 这是 Google 开发的高性能 RPC 框架,原生支持四种通信模式,其中三种为流式:单向流(client/server streaming)、双向流(bidirectional streaming)。
选择 gRPC 而非标准 net/rpc 的原因
标准 net/rpc 基于 Go 的反射和编码(如 gob/json),每次调用都是一次完整封包与解包,无法复用连接、无法中途传递多段数据。而 gRPC 基于 HTTP/2,天然支持多路复用、头部压缩、连接复用和真正的流式语义。实际项目中,凡涉及日志推送、实时指标、消息广播、大文件分块上传/下载等场景,都应优先选用 gRPC。
定义流式 gRPC 接口(.proto 文件)
流式能力由 Protocol Buffers 的 service 定义决定。关键看 stream 关键字位置:
-
客户端流式:
rpc Upload(stream Chunk) returns (Status);(客户端连续发,服务端收完统一回) -
服务端流式:
rpc Subscribe(Request) returns (stream Event);(客户端发一次,服务端持续推多个响应) -
双向流式:
rpc Chat(stream Message) returns (stream Message);(双方可随时发、随时收,独立异步)
定义后用 protoc 工具生成 Go 代码(需安装 protoc-gen-go 和 protoc-gen-go-grpc 插件)。
立即学习“go语言免费学习笔记(深入)”;
服务端实现双向流式逻辑(示例节选)
以双向聊天流为例,服务端需在 handler 中启动 goroutine 处理接收,同时保持发送通道开放:
func (s *chatServer) Chat(stream pb.Chat_ChatServer) error {
// 启动接收协程
go func() {
for {
msg, err := stream.Recv()
if err == io.EOF {
return
}
if err != nil {
log.Printf("recv err: %v", err)
return
}
// 广播或转发逻辑(如存入 map 或发给其他 client)
}
}()
// 主协程负责发送(例如定时广播、或响应某事件)
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
_ = stream.Send(&pb.Message{Content: "server heartbeat"})
case <-stream.Context().Done():
return stream.Context().Err()
}
}}
客户端使用流式客户端(发送+接收并行)
客户端需显式调用 Send() 和 Recv(),通常用两个 goroutine 分离读写:
- 一个 goroutine 循环调用
stream.Send()发送用户输入或心跳 - 另一个 goroutine 循环调用
stream.Recv()读取服务端推送,处理或打印 - 注意检查
io.EOF(流正常结束)和context.Canceled(主动断开)
务必用 stream.CloseSend() 显式关闭发送端(尤其客户端流或双向流),否则服务端可能一直等待。
基本上就这些。gRPC 流式不是“配置开关”,而是由 proto 定义驱动、由生成代码约束、靠手动管理读写 goroutine 实现的协作模型。不复杂但容易忽略上下文控制和错误传播,建议搭配 errgroup 管理多协程生命周期。










