跳转到内容

grpc-go Server 生命周期

grpc.Server 负责把静态服务描述和动态网络连接接起来。它先注册 ServiceDesc,再在 Serve 中接收连接、完成 HTTP/2 transport 初始化,最后按 method 路由到生成代码提供的 handler。

先给答案:grpc.Server 负责把静态服务描述和动态网络连接接起来

Section titled “先给答案:grpc.Server 负责把静态服务描述和动态网络连接接起来”

grpc.Server 负责把静态服务描述和动态网络连接接起来。它先注册 ServiceDesc,再在 Serve 中接收连接、完成 HTTP/2 transport 初始化,最后按 method 路由到生成代码提供的 handler。 正文沿“启动链路 -> 从连接到 RPC -> 优雅停止”展开:先确认入口和状态归属,再跟踪控制流或数据流的推进,最后落到对外可观察的结果。

主要失效边界集中在“Serve 后注册服务、只调用 GracefulStop、Accept 临时错误”这些场景。它们破坏的是容量、顺序、并发或生命周期前提;排查时应先确认状态是否仍由正确对象持有,再核对推进条件和清理路径。

NewServer(options)
|
RegisterService(ServiceDesc, impl)
|
Serve(listener)
+-> Accept
+-> handleRawConn
+-> TLS / HTTP2 handshake
+-> addConn
+-> serveStreams
+-> HandleStreams
+-> handleStream
+-> interceptor -> generated handler

构造位于 server.go:702-754,服务必须在 Serve 前通过 RegisterService 注册:server.go:756-800。Serve 的 Accept 循环会对临时错误指数退避,并为每个原始连接启动独立 goroutine:server.go:896-985。

handleRawConn 设置握手超时、创建 HTTP/2 transport、登记连接,再启动 stream 处理:server.go:990-1016。serveStreams 将 transport 解析出的每个 stream 交给 handleStream:server.go:1061-1088、:1519-1610。

当前实现用 wrapUnaryHandler 把 unary method 转成统一 StreamHandler,因此 unary 和 streaming 可以共享后续处理管线:server.go:1268-1280。processRPC 统一创建 serverStream,挂接 stats、binary log、codec 和 tracing::1282-1430。

GracefulStop
+-> fire quit
+-> close listeners
+-> wait Serve goroutines
+-> send GOAWAY / drain transports
+-> wait active streams
+-> close transports

Stop 与 GracefulStop 共用 stop(graceful bool):server.go:1658-1738。立即停止直接关闭 transport;优雅停止先 drain,等待活跃 RPC 结束。grpc-go 本身不提供超时参数,调用方若需要硬截止,应在超时后再调用 Stop。

场景 风险 处理
Serve 后注册服务 并发路由表不一致 启动前完成全部注册
只调用 GracefulStop 长流让退出永久等待 外层增加超时并兜底 Stop
Accept 临时错误 服务瞬时退出 内部退避重试,关注最终非临时错误
handler panic 进程退出 使用 recovery interceptor

优雅停机不是“关闭 listener”就结束,而是依次阻止新连接、通知现有连接排空、等待活跃请求,再释放 transport。

面试锚点

  • Serve 为什么为 raw connection 和 stream 分别起 goroutine?
  • unary RPC 为什么也能走统一 stream 管线?
  • GracefulStop 为什么需要业务层超时兜底?