5月27日 23:30

gRPC 的核心特性与优势是什么?

gRPC 是什么

gRPC 是 Google 开源的高性能 RPC 框架,基于 HTTP/2 传输协议和 Protocol Buffers 序列化格式构建。它不仅是一个远程调用工具,更是一套完整的跨语言服务通信方案——通过 .proto 文件定义接口,自动生成多语言客户端和服务端桩代码,让不同语言的服务之间高效互通。

核心特性

HTTP/2 传输层

HTTP/2 是 gRPC 高性能的底层基石,带来三个关键能力:

  • 多路复用:一条 TCP 连接上并行收发多个请求/响应,彻底解决 HTTP/1.1 的队头阻塞问题。实际效果是微服务间无需维护大量连接池,单连接即可支撑高并发调用。
  • 头部压缩(HPACK):对请求头进行差分编码压缩,频繁调用的场景下头部开销可降低 80% 以上,这对移动端和低带宽环境尤其重要。
  • 流式传输:HTTP/2 的 Stream 机制天然支持服务端流、客户端流和双向流三种流式 RPC 模式。

Protocol Buffers 序列化

Protobuf 是 gRPC 默认的 IDL 和序列化方案:

  • 二进制编码:相比 JSON 的文本格式,Protobuf 编码体积通常小 3-10 倍,序列化速度快 5 倍以上。原理是采用变长整数(varint)和字段编号而非字段名来标识数据。
  • 强类型约束:.proto 文件就是接口契约,编译时就能发现类型不匹配的问题,而不是等到运行时才报错。
  • 向后兼容:新增字段只分配新编号,老代码读到未知字段自动跳过,这在微服务滚动升级中至关重要。
  • 跨语言代码生成:一个 .proto 文件可以生成 Go、Java、Python、C++ 等 10+ 语言的客户端和服务端代码,保证多语言团队的接口一致性。

四种服务模式

gRPC 定义了四种通信模式,覆盖从简单请求到实时交互的全部场景:

模式客户端服务端典型场景
一元 RPC(Unary)单个请求单个响应查询用户信息
服务端流式单个请求流式响应订阅实时行情
客户端流式流式请求单个响应批量上传文件
双向流式流式请求流式响应聊天、游戏同步

为什么选择 gRPC 而不是 REST 或其他 RPC 框架

gRPC vs REST

维度gRPCREST
传输协议HTTP/2HTTP/1.1 为主
数据格式Protobuf 二进制JSON 文本
流式通信四种模式原生支持需 WebSocket 或 SSE
接口定义.proto 文件强约束OpenAPI 规范(可选)
代码生成自动生成多语言桩代码需第三方工具
浏览器支持需 gRPC-Web 代理原生支持

核心结论:内部微服务间通信选 gRPC,对外 API 尤其是面向浏览器/移动端选 REST。两者不是互斥的,很多团队用 gRPC 做内部通信,同时通过 gRPC-Gateway 暴露 REST 接口给外部。

gRPC vs Thrift

Thrift 同样支持二进制序列化和多语言,但 gRPC 的优势在于 HTTP/2 原生支持(Thrift 通常走 TCP 自定义协议)、流式通信、以及 Google 生态(Kubernetes、Envoy 原生支持 gRPC 健康检查和负载均衡)。

gRPC 的核心优势

性能突出:HTTP/2 多路复用 + Protobuf 二进制编码,端到端延迟通常比 REST+JSON 低 30%-50%,吞吐量提升 5-10 倍。

开发效率高:写好 .proto 文件后,一行命令生成所有语言的客户端和服务端代码,接口变更时重新生成即可,无需手工同步。

跨语言无缝集成:多语言微服务架构中,Go 写网关、Java 写业务、Python 写算法服务,都用同一份 .proto 定义,类型安全、调用方式统一。

流式通信能力:双向流是 gRPC 独特的杀手锏,让实时推送、聊天、监控等场景的实现从 hack 变成标准用法。

生态成熟:拦截器(Interceptor)实现鉴权和日志、健康检查协议配合 Kubernetes、内置超时和重试机制、与 OpenTelemetry 链路追踪无缝集成。

代码示例

定义服务和消息:

protobuf
service OrderService { // 一元调用:查询订单 rpc GetOrder(GetOrderRequest) returns (Order) {} // 服务端流:订阅订单状态变更 rpc SubscribeOrder(SubscribeRequest) returns (stream OrderStatus) {} // 客户端流:批量创建订单 rpc BatchCreateOrders(stream CreateOrderRequest) returns (BatchResult) {} // 双向流:实时议价 rpc Negotiate(stream PriceRequest) returns (stream PriceResponse) {} } message GetOrderRequest { string order_id = 1; } message Order { string order_id = 1; string status = 2; int64 created_at = 3; }

Go 语言服务端实现核心逻辑:

go
func (s *Server) GetOrder(ctx context.Context, req *pb.GetOrderRequest) (*pb.Order, error) { order, err := s.repo.FindByID(ctx, req.OrderId) if err != nil { return nil, status.Errorf(codes.NotFound, "order %s not found", req.OrderId) } return order, nil } func (s *Server) SubscribeOrder(req *pb.SubscribeRequest, stream pb.OrderService_SubscribeOrderServer) error { ch := s.eventBus.Subscribe(req.OrderId) for status := range ch { if err := stream.Send(status); err != nil { return err } } return nil }

面试追问方向

  • gRPC 的 HTTP/2 多路复用如何避免队头阻塞?和 HTTP/1.1 的队头阻塞有什么本质区别?
  • Protobuf 的 varint 编码原理是什么?负数如何处理?
  • gRPC 的拦截器分哪两种?分别用在什么场景?
  • gRPC-Web 的原理是什么?为什么浏览器不能直接调用 gRPC?
  • 生产环境中 gRPC 连接如何做负载均衡?为什么客户端负载均衡更常见?

面试中回答 gRPC 问题,关键是把特性说清楚、把和 REST 的对比讲透、把流式通信的场景用实际例子说明,而不是泛泛罗列特性。掌握了 HTTP/2 和 Protobuf 两个底层原理,其他问题都能顺理成章地展开。

标签:RPC