这份文档整理了 gRPC 面试中从基础概念到生产实践的全部高频考点,覆盖了初级、中级、高级面试的所有常见问题,帮助你全面准备 gRPC 相关的技术面试。
一、基础概念篇(入门必问)
1. 什么是 gRPC?它解决了什么问题?
gRPC(Google Remote Procedure Call) 是 Google 开源的高性能、通用的远程过程调用(RPC)框架,基于 HTTP/2 协议传输,默认使用 Protocol Buffers(Protobuf)作为序列化格式。
它主要解决了分布式系统中服务间通信的核心痛点:
-
跨语言通信:支持 C++、Go、Java、Python、Ruby 等十余种语言,异构系统可以无缝集成
-
高性能传输:相比传统的 REST+JSON,提供了 5-10 倍的性能提升
-
标准化接口:通过 Protobuf 定义强类型接口,消除了接口文档与代码不一致的问题
-
流式通信:支持双向流,满足实时通信、大数据传输等复杂场景
2. gRPC 和 REST API 有什么核心区别?
这是面试最高频的对比题,建议用表格清晰回答:
| 对比维度 | REST API (HTTP/1.1 + JSON) | gRPC (HTTP/2 + Protobuf) |
|---|---|---|
| 传输协议 | HTTP/1.1 | HTTP/2 |
| 序列化格式 | JSON(文本格式) | Protobuf(二进制格式) |
| 接口契约 | OpenAPI(可选,弱约束) | .proto 文件(强制,强类型) |
| 通信模式 | 仅支持简单请求 - 响应 | 支持 4 种模式:一元、服务端流、客户端流、双向流 |
| 连接模型 | 每个请求可能新建连接,队头阻塞 | 单 TCP 连接多路复用,并发处理多个请求 |
| 性能 | 较低,文本解析慢、体积大 | 高,二进制序列化 + 多路复用,性能提升 5-10 倍 |
| 浏览器支持 | 原生支持 | 需要 gRPC-Web 或网关转换 |
| 调试成本 | 低,文本可读 | 较高,二进制需要专用工具 |
- 补充:
-
虽然 gRPC 在性能和功能上有明显优势,但 REST API 由于其简单、易于调试和广泛的工具支持,在很多场景下仍然非常流行,特别是对外 API 和浏览器交互的场景。
-
由于 gRPC 的调试成本较高,建议在开发阶段使用 gRPC 的调试工具,比如 BloomRPC、gRPCurl 等,或者在服务端实现一个 HTTP 网关,提供 REST API 供调试使用。等到生产环境后,再切换到 gRPC 的高性能通信。
-
3. 什么是 Protocol Buffers?为什么 gRPC 选择它?
Protocol Buffers(简称 Protobuf)是 Google 推出的一种轻量级、高效的结构化数据序列化格式,是一种接口定义语言(IDL)。
gRPC 选择它的原因:
-
极致的性能:二进制序列化,比 JSON 小 3-10 倍,解析速度快 5-100 倍
-
跨语言兼容:一份 proto 文件可以自动生成多语言的代码,保证接口一致性
-
强类型契约:编译期就能检查接口错误,避免运行时的类型错误
-
向后兼容:通过字段编号实现平滑的版本升级,不破坏老客户端
-
代码生成:自动生成客户端和服务端的样板代码,减少开发工作量
4. Protobuf 的序列化原理是什么?为什么它比 JSON 小?
Protobuf 提供了5种基本数据类型:int32、int64、uint32、uint64、bool、string 等,还有复合类型 message 和枚举 enum。
Protobuf 的核心序列化逻辑是用字段编号代替字段名传输:
|
|
序列化时,每个字段会被编码为:(field_number << 3 | wire_type) + value,将field_number左移3位,和wire_type(表示数据类型)进行按位或运算,得到一个唯一的标识符,然后紧跟着字段值的二进制编码。也就是说:
-
不需要传输
"id"、"name"这些字符串字段名,只传 1、2 这样的数字编号 -
不需要 JSON 里的引号、冒号、逗号这些分隔符
-
数字类型直接存储二进制,不需要转成文本字符串
所以 Protobuf 的序列化结果体积远小于 JSON,而且解析速度更快。
5. gRPC 支持哪些编程语言?如何保证跨语言兼容?
gRPC 官方支持 C++、Go、Java、Python、Ruby、C#、Objective-C、PHP、Dart、Rust 等十余种主流编程语言。
跨语言兼容的核心是:
-
统一的接口定义:所有语言都基于同一份.proto 文件定义接口
-
标准化的序列化:Protobuf 定义了统一的二进制编码格式,不同语言都按照相同的规则序列化 / 反序列化
-
自动生成代码:protoc 编译器会根据 proto 文件,为不同语言生成对应的客户端 / 服务端代码,保证接口的一致性
二、核心通信模式篇(高频重点)
1. gRPC 支持哪四种通信模式?分别的使用场景是什么?
这是面试必考题,必须完整说出四种模式,以及对应的场景:
1.1 一元 RPC(Unary RPC)
-
模式:客户端发送 1 个请求,服务端返回 1 个响应,就是最普通的 RPC 调用
-
场景:普通的查询、增删改查接口,比如获取用户信息、创建订单
-
类比:普通的 HTTP POST 请求
1.2 服务端流 RPC(Server-side Streaming RPC)
-
模式:客户端发送 1 个请求,服务端返回多个响应的流
-
场景:
-
大文件下载:服务端边读边发,不需要一次性把整个文件加载到内存
-
实时数据推送:比如股票行情推送、监控数据推送
-
批量数据查询:比如查询百万级数据,服务端分批返回
-
-
proto 定义:
|
|
1.3 客户端流 RPC(Client-side Streaming RPC)
-
模式:客户端发送多个请求的流,服务端返回 1 个响应
-
场景:
-
大文件上传:客户端边读边发,不需要一次性上传整个文件
-
批量数据导入:比如批量上报监控数据、日志数据
-
-
proto 定义:
|
|
1.4 双向流 RPC(Bidirectional Streaming RPC)
-
模式:客户端和服务端都可以独立发送多个消息的流,双方可以并发读写
-
场景:
-
实时聊天:双方可以随时发消息
-
实时协作:比如在线文档的协同编辑
-
双向实时通信:比如游戏中的状态同步
-
-
proto 定义:
|
|
2. 双向流是如何实现并发读写的?
双向流基于 HTTP/2 的全双工通信能力实现:
-
HTTP/2 的单个 TCP 连接上,流是双向的,双方可以同时发送数据
-
gRPC 的流对象提供了独立的 Send 和 Recv 方法,两者互不阻塞
-
客户端和服务端可以在各自的 goroutine / 线程中独立处理读写,不需要等待对方
3. 流模式下的流量控制(Flow Control)是如何实现的?
gRPC 的流控主要依赖底层 HTTP/2 的流量控制机制:
-
窗口大小机制:每个流和整个连接都有一个接收窗口,接收方通过
WINDOW_UPDATE帧告诉发送方自己还能接收多少数据 -
背压(Backpressure):当接收方处理不过来的时候,会减小窗口大小,发送方就会自动减慢发送速度,避免把接收方压垮
-
优先级调度:HTTP/2 支持流的优先级,重要的流可以优先传输
4. 流模式下如何优雅关闭流?
-
客户端 / 服务端完成发送后,调用
CloseSend()方法关闭发送方向的流 -
接收方读完所有消息后,会收到 EOF,标识流结束
-
对于错误场景,通过发送
RST_STREAM帧来重置流,中断整个流的通信 -
必须保证所有的消息都处理完成后再关闭,避免数据丢失
三、底层原理篇(深入必问)
1. 为什么 gRPC 选择 HTTP/2 而不是 HTTP/1.1?
HTTP/2 的特性是 gRPC 高性能的核心基础:
-
多路复用(Multiplexing):解决了 HTTP/1.1 的队头阻塞问题,单个 TCP 连接可以同时处理多个请求 / 响应,请求和响应可以交错传输,不需要为每个请求新建 TCP 连接
-
二进制分帧:把数据拆分为更小的帧,更高效的传输,天然支持流式数据
-
头部压缩(HPACK):对 HTTP 头部进行压缩,减少重复头部的传输开销,比如每次请求都带的认证 token、user-agent 这些
-
流控与优先级:支持流量控制和流优先级,更好的管理资源
-
服务器推送:服务端可以主动向客户端推送资源,支持双向通信
2. HTTP/2 的队头阻塞是什么?gRPC 是怎么解决的?
HTTP/1.1 中,同一个 TCP 连接上的请求必须排队,必须等前一个请求的响应完全返回后,才能处理下一个请求,如果前一个请求阻塞了,后面的所有请求都被阻塞,这就是队头阻塞(Head of Line Blocking)。
gRPC 基于 HTTP/2 的多路复用解决了这个问题:
-
每个 RPC 调用对应一个独立的 Stream,每个 Stream 有唯一的 ID
-
不同 Stream 的消息可以拆分成帧,交错传输,互不影响
-
即使某个 Stream 的消息阻塞了,其他 Stream 的消息依然可以正常传输
-
这样单个 TCP 连接就可以同时处理成百上千个 RPC 请求,极大的提升了连接的利用率
3. gRPC、HTTP/2、Stream 三者的关系是什么?
这三者是分层的关系:
-
HTTP/2:是底层的传输协议,提供了多路复用、流控、二进制分帧这些基础能力
-
Stream:是 HTTP/2 中的逻辑通道,每个 Stream 对应一个独立的 RPC 调用,多个 Stream 可以复用同一个 TCP 连接
-
gRPC:是基于 HTTP/2 的 RPC 框架,它把每个 RPC 调用映射到一个 HTTP/2 的 Stream 上,利用 Stream 的双向传输能力实现了四种通信模式
简单来说:gRPC 利用 HTTP/2 的 Stream 机制,实现了 RPC 的调用和流式通信。
4. HTTP/2 有哪些常见的帧类型?各有什么作用?
HTTP/2 以帧为最小传输单位,常见的帧类型:
-
DATA 帧:传输应用层的业务数据
-
HEADERS 帧:传输 HTTP 头信息,gRPC 的 Metadata 就是通过这个帧传输的
-
PRIORITY 帧:设置流的优先级
-
RST_STREAM 帧:重置流,用于中断流、处理错误
-
SETTINGS 帧:配置连接的参数,比如窗口大小、最大帧大小
-
PUSH_PROMISE 帧:服务端推送的预告帧
-
PING 帧:心跳检测,测量往返时间
-
GOAWAY 帧:通知对端,连接即将关闭,不要再新建流了
-
WINDOW_UPDATE 帧:更新流量控制的窗口大小
5. gRPC 的高性能的原因是什么?
这是高频题,总结起来有三点:
-
序列化层:Protobuf 二进制序列化,体积小、解析快,比 JSON 高效很多
-
传输层:HTTP/2 的多路复用,连接复用,减少 TCP 握手开销,解决队头阻塞
-
应用层:强类型契约,编译期生成代码,没有运行时反射开销,异步非阻塞 IO 模型
四、核心功能与组件篇(开发必问)
1. 什么是拦截器(Interceptor)?作用是什么?
拦截器是 gRPC 中用来拦截请求 / 响应的中间层,类似 Gin 的中间件,它可以在请求到达业务方法之前,或者响应返回之前,执行自定义的逻辑。
拦截器分为两种:
-
一元拦截器:处理普通的一元 RPC 调用
-
流拦截器:处理流式 RPC 调用
常见的用途:
-
认证与授权:统一验证 token,不需要每个业务方法都写
-
日志记录:统一记录请求日志、响应时间
-
链路追踪:添加 trace 信息,传递追踪上下文
-
监控 metrics:收集请求的 QPS、延迟、错误率
-
异常捕获:统一捕获 panic,转换成标准的错误响应
-
请求转换:统一修改请求头、请求参数
2. 什么是 Metadata?它是用来做什么的?
Metadata 是 gRPC 中用来传递请求的附加元数据的键值对,它不会作为请求体的一部分,而是随请求一起传输,类似 HTTP 的 Header。
它的作用:
-
传递认证信息:比如 JWT token、API Key
-
传递链路追踪信息:比如 Trace ID、Span ID
-
传递请求的附加信息:比如用户语言、客户端版本
-
传递调试信息:比如调试标记
Metadata 是通过 HTTP/2 的 HEADERS 帧传输的,客户端和服务端都可以发送和接收 Metadata。
3. 什么是 Channel?Channel 和 Connection 的关系是什么?
Channel 是 gRPC 客户端的核心抽象,它代表了客户端到服务端的虚拟连接,封装了连接管理、负载均衡、服务发现等逻辑。
Channel 和 Connection 的关系:
-
一个 Channel 可以对应多个 TCP Connection
-
在客户端负载均衡的模式下,Channel 会管理到多个服务端实例的连接
-
Channel 是线程安全的,可以被多个 goroutine 并发使用,通常一个服务端的客户端只需要创建一个 Channel 就够了,不需要每次调用都新建
-
Channel 会自动处理连接的重连、健康检查
4. 什么是 Resolver(解析器)?常见的实现有哪些?
Resolver 是 gRPC 中负责服务发现的组件,它的作用是把服务名称解析成实际的服务端网络地址列表。
常见的 Resolver 实现:
-
DNS Resolver:通过 DNS 解析服务地址
-
etcd Resolver:从 etcd 中获取服务注册的地址,常用在微服务中
-
Consul/Nacos Resolver:从 Consul/Nacos 这些服务注册中心获取地址
-
自定义 Resolver:开发者可以自己实现解析逻辑,比如从配置中心获取地址
Resolver 会持续监听服务地址的变化,当服务端实例上下线时,自动更新客户端的地址列表,实现服务的动态发现。
5. 什么是 Balancer(负载均衡器)?常见的策略有哪些?
Balancer 是 gRPC 中负责负载均衡的组件,当有多个服务端实例时,它负责选择一个实例来处理客户端的请求。
常见的负载均衡策略:
-
pick_first:默认策略,选择第一个可用的地址,所有请求都发往这个地址
-
round_robin:轮询,按顺序把请求分发到不同的实例
-
weighted_round_robin:加权轮询,根据实例的权重分发请求
-
least_request:最小请求数,选择当前正在处理的请求最少的实例
-
p2c(Power of Two Choices):随机选两个实例,选择其中负载更低的那个,go-zero 默认用的就是这个,性能很好
6. 什么是 Deadline/Timeout?它的作用是什么?
Deadline 是 gRPC 中用来设置请求的截止时间的机制,它表示这个请求必须在某个时间点之前完成,否则就会超时返回DEADLINE_EXCEEDED错误。
它的核心作用:
-
防止资源泄露:避免请求因为网络问题一直挂起,占用服务端的资源
-
防止服务雪崩:当服务端响应慢的时候,客户端不会一直等待,快速失败,避免把服务端压垮
-
链路传递:Deadline 会随着 context 在整个微服务调用链中传递,整个调用链共享同一个截止时间,比如网关设置了 3s 的超时,那么下游的所有服务都会自动感知到剩余的时间,不会做无效的处理
注意:Deadline 是绝对时间,不是相对时间,会跨服务传递,所以不需要每个服务都单独设置超时。
7. gRPC 的重试策略有哪些?
gRPC 的重试机制主要有这些策略:
-
固定间隔重试:每次失败后,等待固定的时间再重试
-
指数退避重试:每次失败后,等待时间指数级增长,比如第一次等 100ms,第二次 200ms,第三次 400ms,避免重试风暴
-
错误过滤:不是所有错误都重试,只对可恢复的错误重试,比如
Unavailable、DeadlineExceeded,而参数错误、AlreadyExists 这些不可恢复的错误就不重试 -
最大重试次数:限制最大重试次数,避免无限重试
8. 什么是 gRPC 的健康检查?
健康检查是 gRPC 提供的标准服务,用来检查服务端的健康状态,客户端或者负载均衡器可以通过这个接口来判断服务是否可用。
健康检查的状态有三种:
-
SERVING:服务正常,可以处理请求 -
NOT_SERVING:服务不可用,无法处理请求 -
UNKNOWN:未知状态,无法确定服务的状态
健康检查可以用来做服务的自动摘除,当服务不健康的时候,负载均衡器会自动把它从可用列表中移除,避免把请求发过去。
五、安全与认证篇(进阶必问)
1. gRPC 支持哪些安全机制?
gRPC 提供了完善的安全机制:
-
TLS/SSL 加密:对传输的数据进行加密,防止中间人攻击
-
mTLS(双向 TLS):客户端和服务端互相验证证书,常用于服务间的认证
-
Token 认证:通过 Metadata 传递 JWT、OAuth2 token,做用户级的认证
-
拦截器认证:通过拦截器统一验证 token,实现授权逻辑
2. Channel credentials 和 Call credentials 有什么区别?
这是 gRPC 中两种不同的认证凭证:
-
Channel credentials:是针对整个连接的凭证,比如 TLS 证书,在建立连接的时候使用,对整个连接的所有 RPC 调用都生效
-
Call credentials:是针对单个 RPC 调用的凭证,比如 JWT token,每个请求都可以带不同的 token,放在 Metadata 中传输
3. 如何在 gRPC 中实现 JWT 认证?
通常通过拦截器来实现:
-
客户端拦截器:在发送请求之前,把 JWT token 添加到 Metadata 中
-
服务端拦截器:在请求到达业务方法之前,从 Metadata 中取出 token,验证 token 的有效性,如果验证失败,直接返回未认证的错误,不需要进入业务方法
这样就可以统一处理所有接口的认证,不需要每个业务方法都写认证逻辑。
4. 什么是 mTLS?它和普通 TLS 有什么区别?
普通 TLS 是单向的,只有客户端验证服务端的证书,确认服务端是可信的;而 mTLS(双向 TLS)是客户端和服务端互相验证对方的证书,双方都要确认对方是可信的。
mTLS 常用于服务间的通信,比如微服务架构中,只有拥有合法证书的服务才能互相调用,防止非法的服务接入,提升系统的安全性。
六、服务治理篇(生产必问)
1. gRPC 如何做服务发现?
gRPC 提供了 Resolver 的扩展接口,我们可以集成服务注册中心:
-
服务端启动的时候,把自己的地址注册到 etcd/Consul/Nacos 这些注册中心,带上租约
-
客户端通过自定义的 Resolver,从注册中心获取服务的地址列表
-
Resolver 会监听注册中心的变化,当服务端实例上下线的时候,自动更新客户端的地址列表
-
客户端的 Balancer 根据更新后的地址列表,做负载均衡
这样就实现了服务的动态发现,不需要手动配置服务端的地址。
2. gRPC 的客户端负载均衡和服务端负载均衡有什么区别?
-
客户端负载均衡:客户端自己维护服务端的地址列表,自己选择要调用的实例,不需要中间的负载均衡器,减少了一跳网络,性能更好,比如 gRPC 的内置 Balancer 就是这种
-
服务端负载均衡:客户端把请求发给统一的负载均衡器,由负载均衡器选择服务端实例,比如 Nginx、Istio,这种方式对客户端透明,但是多了一跳网络
3. gRPC 中如何实现熔断、限流、降级?
这些都是微服务的服务保护机制,在 gRPC 中通常通过拦截器或者框架来实现:
-
限流:通过拦截器,在请求进入的时候,用令牌桶或者计数器算法,限制单位时间的请求数,超过的直接拒绝
-
熔断:当某个服务的错误率达到阈值,熔断器打开,后续的请求直接快速失败,避免把下游压垮,等冷却时间后再探测恢复
-
降级:当服务压力大的时候,关闭非核心的功能,或者返回默认的缓存数据,保证核心功能可用
比如 go-zero 框架就内置了这些能力,也可以自己通过拦截器实现。
4. gRPC 的重试为什么要配合幂等性?
因为网络超时的时候,我们不确定请求是否已经到达服务端并执行了:
-
如果请求已经执行成功,只是响应丢了,这时候重试就会导致重复执行,比如重复扣款、重复下单
-
所以重试必须配合幂等性,保证同一个请求执行多次和执行一次的效果是一样的
常见的幂等实现:
-
幂等 Token:客户端生成唯一的请求 ID,服务端用这个 ID 做去重,缓存执行结果
-
数据库唯一键:比如订单的请求 ID 加唯一索引,重复插入的时候会捕获错误,返回已有结果
-
状态机:通过业务状态来控制,比如订单状态已经是已支付,就不会重复处理
七、版本管理与兼容性篇(架构必问)
1. Proto 文件的字段编号为什么不能随意修改?
因为 Protobuf 序列化的时候,是用字段编号来标识字段的,不是字段名:
-
如果服务端修改了字段编号,老客户端发送的数据,服务端就会解析到错误的字段上,导致数据错乱
-
比如老版本中
id=1,name=2,服务端改成name=1,id=2,那么老客户端发的 id=1 的数据,服务端就会解析到 name 字段上,完全乱了
正确的版本升级做法:
-
只新增字段,不要修改已有字段的编号
-
废弃的字段,用
reserved关键字保留编号,不要复用 -
这样就能保证向后兼容,老客户端可以正常访问新服务,新客户端也可以访问老服务
2. gRPC 服务如何做版本管理?
常见的版本管理方式:
-
包版本:把不同版本的服务放在不同的 proto 包中,比如
package helloworld.v1和package helloworld.v2,两个版本的服务可以共存,客户端可以选择调用哪个版本 -
URL 版本:把版本放在服务的地址中,比如不同版本的服务监听不同的端口,或者不同的路径
-
字段兼容:对于小的非破坏性变更,通过新增字段来实现,不需要升级版本号,利用 Protobuf 的向后兼容能力
3. Protobuf 如何保证向后兼容?
Protobuf 的设计天然支持向后兼容:
-
新增的字段,老客户端会忽略,老服务端也会忽略新客户端发的新字段
-
废弃的字段,只要保留编号,就不会影响老客户端
-
可以把字段从可选改成重复,或者反过来,只要类型兼容
-
只要不修改已有字段的编号和类型,就不会破坏兼容性
八、调试、监控与运维篇(运维必问)
1. gRPC 的二进制不好调试,有什么调试工具?
有很多专用的调试工具:
-
grpcurl:命令行工具,类似 curl,可以直接调用 gRPC 接口
-
BloomRPC/Postman/Insomnia:GUI 工具,可以可视化的调用 gRPC 接口,查看请求和响应
-
gRPC reflection:服务端开启反射服务,客户端就可以动态获取服务的接口定义,不需要 proto 文件,调试工具就是靠这个来工作的
-
grpc_cli:官方的命令行调试工具
2. gRPC 如何做监控和链路追踪?
-
Metrics 监控:通过拦截器收集请求的 QPS、延迟、错误率、流量等指标,上报到 Prometheus 等监控系统
-
链路追踪:通过拦截器,把 OpenTelemetry/Jaeger/Zipkin 的 Trace 信息放到 Metadata 中,跨服务传递,实现整个调用链的追踪
-
日志:通过拦截器统一记录请求的日志,包括方法名、客户端地址、延迟、错误信息等
3. gRPC 的健康检查是如何工作的?
gRPC 提供了标准的Health服务,定义了一个Check方法,客户端可以调用这个方法来检查服务的健康状态:
-
服务端实现这个方法,返回自己的健康状态
-
负载均衡器或者服务注册中心会定期调用这个接口,来判断服务是否可用
-
如果健康检查失败,就会把服务从可用列表中移除
4. gRPC 服务的压测需要关注哪些指标?
压测的时候需要关注:
-
QPS:服务能承载的最大每秒请求数
-
延迟 P99/P999:请求的延迟分布,长尾延迟很重要
-
错误率:请求的错误比例
-
CPU / 内存使用率:服务端的资源使用情况
-
连接数:TCP 连接的数量,是否有连接泄露
-
GC 情况:对于 Go/Java 这些有 GC 的语言,要关注 GC 的停顿时间
九、生产实践与常见问题篇(踩坑必问)
1. gRPC 有什么缺点?
gRPC 不是银弹,它也有缺点:
-
调试成本高:二进制格式,不能直接用 curl 调试,需要专用工具
-
浏览器不支持:浏览器原生不支持 HTTP/2 的双向流,需要用 gRPC-Web 或者网关转换
-
学习成本高:需要学习 Protobuf 的语法,还有 gRPC 的相关概念
-
Proto 文件管理:多服务的时候,需要管理 proto 文件的版本和依赖
-
对老系统不友好:一些老旧的网络设备可能不支持 HTTP/2
2. 什么场景下适合用 gRPC?什么场景下不适合?
适合的场景:
-
微服务内部的服务间通信,需要高性能
-
实时通信场景,比如聊天、实时推送、协同编辑
-
多语言的异构系统,需要跨语言调用
-
大数据传输场景,比如文件上传下载、批量数据同步
不适合的场景:
-
对外的简单 API,需要浏览器直接调用的场景
-
对调试要求很高,需要人类可读的接口的场景
-
非常简单的小服务,用 REST 就够了,没必要引入 gRPC 的复杂度
3. gRPC 和 Thrift 有什么区别?
两者都是 RPC 框架,区别在于:
-
传输协议:gRPC 基于 HTTP/2,可以更好的兼容现有的网络基础设施,而 Thrift 通常用自己的 TCP 协议
-
序列化:gRPC 用 Protobuf,Thrift 有自己的序列化格式,性能差不多
-
生态:gRPC 的生态更完善,有负载均衡、服务发现、网关、监控这些配套的工具
-
流式支持:gRPC 的流式更成熟,基于 HTTP/2 的流,而 Thrift 也有流,但是用的少
4. 常见的 gRPC 错误排查
-
protoc: command not found:protoc 没有安装,或者没有加到 PATH
-
Plugin protoc-gen-go not found:Go 的插件没有安装,或者 GOPATH/bin 没有加到 PATH
-
连接超时:检查服务端是否启动,端口是否开放,防火墙是否拦截,Docker 的端口映射是否正确
-
模块路径错误:导入 pb 包的时候,要替换成自己的 go mod 的模块路径
-
流关闭错误:流没有正确关闭,导致资源泄露,要确保用完后关闭流
-
max message size exceeded:消息太大,超过了默认的 4MB 限制,需要调整
max_receive_message_size参数
十、高频面试真题汇总(速查)
-
Q:gRPC 为什么比 REST 快? A:三个原因:Protobuf 二进制序列化小且快;HTTP/2 多路复用减少连接开销;强类型无反射开销。
-
Q:gRPC 的四种通信模式是什么? A:一元 RPC、服务端流、客户端流、双向流,分别对应不同的场景。
-
Q:什么是拦截器?作用是什么? A:中间层组件,用来统一处理认证、日志、监控等逻辑,类似中间件。
-
Q:HTTP/2 的多路复用解决了什么问题? A:解决了 HTTP/1.1 的队头阻塞,单连接可以并发处理多个请求,提升连接利用率。
-
Q:Deadline 的作用是什么? A:设置请求的截止时间,防止资源泄露,防止服务雪崩,而且会跨服务链路传递。
-
Q:Proto 的字段编号为什么不能改? A:因为序列化用编号标识字段,改了会导致新旧版本解析错乱,破坏兼容性。
-
Q:gRPC 的负载均衡有哪些策略? A:轮询、加权轮询、最小请求、P2C 等。
-
Q:gRPC 如何做认证? A:TLS 加密、mTLS 双向认证、Token 认证,通过拦截器统一处理。
-
Q:Metadata 是什么? A:传递请求的元数据,类似 HTTP Header,用来传 token、trace 信息等。
-
Q:gRPC 的缺点是什么? A:调试难、浏览器不支持、学习成本高、proto 管理成本。