全部文章 笔记与思考

关于 gRPC 面试

这份文档整理了 gRPC 面试中从基础概念到生产实践的全部高频考点,覆盖了初级、中级、高级面试的所有常见问题,帮助你全面准备 gRPC 相关的技术面试。


一、基础概念篇(入门必问)

1. 什么是 gRPC?它解决了什么问题?

gRPC(Google Remote Procedure Call) 是 Google 开源的高性能、通用的远程过程调用(RPC)框架,基于 HTTP/2 协议传输,默认使用 Protocol Buffers(Protobuf)作为序列化格式。

它主要解决了分布式系统中服务间通信的核心痛点:

  1. 跨语言通信:支持 C++、Go、Java、Python、Ruby 等十余种语言,异构系统可以无缝集成

  2. 高性能传输:相比传统的 REST+JSON,提供了 5-10 倍的性能提升

  3. 标准化接口:通过 Protobuf 定义强类型接口,消除了接口文档与代码不一致的问题

  4. 流式通信:支持双向流,满足实时通信、大数据传输等复杂场景

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 选择它的原因:

  1. 极致的性能:二进制序列化,比 JSON 小 3-10 倍,解析速度快 5-100 倍

  2. 跨语言兼容:一份 proto 文件可以自动生成多语言的代码,保证接口一致性

  3. 强类型契约:编译期就能检查接口错误,避免运行时的类型错误

  4. 向后兼容:通过字段编号实现平滑的版本升级,不破坏老客户端

  5. 代码生成:自动生成客户端和服务端的样板代码,减少开发工作量

4. Protobuf 的序列化原理是什么?为什么它比 JSON 小?

Protobuf 提供了5种基本数据类型:int32、int64、uint32、uint64、bool、string 等,还有复合类型 message 和枚举 enum。

Protobuf 的核心序列化逻辑是用字段编号代替字段名传输:

1
2
3
4
message User {
  int64 id = 1;  // 1是字段编号,不是值!
  string name = 2;
}

序列化时,每个字段会被编码为:(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 等十余种主流编程语言。

跨语言兼容的核心是:

  1. 统一的接口定义:所有语言都基于同一份.proto 文件定义接口

  2. 标准化的序列化:Protobuf 定义了统一的二进制编码格式,不同语言都按照相同的规则序列化 / 反序列化

  3. 自动生成代码:protoc 编译器会根据 proto 文件,为不同语言生成对应的客户端 / 服务端代码,保证接口的一致性


二、核心通信模式篇(高频重点)

1. gRPC 支持哪四种通信模式?分别的使用场景是什么?

这是面试必考题,必须完整说出四种模式,以及对应的场景:

1.1 一元 RPC(Unary RPC)

  • 模式:客户端发送 1 个请求,服务端返回 1 个响应,就是最普通的 RPC 调用

  • 场景:普通的查询、增删改查接口,比如获取用户信息、创建订单

  • 类比:普通的 HTTP POST 请求

1.2 服务端流 RPC(Server-side Streaming RPC)

  • 模式:客户端发送 1 个请求,服务端返回多个响应的流

  • 场景:

    • 大文件下载:服务端边读边发,不需要一次性把整个文件加载到内存

    • 实时数据推送:比如股票行情推送、监控数据推送

    • 批量数据查询:比如查询百万级数据,服务端分批返回

  • proto 定义:

1
rpc ListUsers(ListUsersRequest) returns (stream UserResponse);

1.3 客户端流 RPC(Client-side Streaming RPC)

  • 模式:客户端发送多个请求的流,服务端返回 1 个响应

  • 场景:

    • 大文件上传:客户端边读边发,不需要一次性上传整个文件

    • 批量数据导入:比如批量上报监控数据、日志数据

  • proto 定义:

1
rpc Upload(stream UploadRequest) returns (UploadResponse);

1.4 双向流 RPC(Bidirectional Streaming RPC)

  • 模式:客户端和服务端都可以独立发送多个消息的流,双方可以并发读写

  • 场景:

    • 实时聊天:双方可以随时发消息

    • 实时协作:比如在线文档的协同编辑

    • 双向实时通信:比如游戏中的状态同步

  • proto 定义:

1
rpc Chat(stream ChatMessage) returns (stream ChatMessage);

2. 双向流是如何实现并发读写的?

双向流基于 HTTP/2 的全双工通信能力实现:

  1. HTTP/2 的单个 TCP 连接上,流是双向的,双方可以同时发送数据

  2. gRPC 的流对象提供了独立的 Send 和 Recv 方法,两者互不阻塞

  3. 客户端和服务端可以在各自的 goroutine / 线程中独立处理读写,不需要等待对方

3. 流模式下的流量控制(Flow Control)是如何实现的?

gRPC 的流控主要依赖底层 HTTP/2 的流量控制机制:

  1. 窗口大小机制:每个流和整个连接都有一个接收窗口,接收方通过WINDOW_UPDATE帧告诉发送方自己还能接收多少数据

  2. 背压(Backpressure):当接收方处理不过来的时候,会减小窗口大小,发送方就会自动减慢发送速度,避免把接收方压垮

  3. 优先级调度:HTTP/2 支持流的优先级,重要的流可以优先传输

4. 流模式下如何优雅关闭流?

  1. 客户端 / 服务端完成发送后,调用CloseSend()方法关闭发送方向的流

  2. 接收方读完所有消息后,会收到 EOF,标识流结束

  3. 对于错误场景,通过发送RST_STREAM帧来重置流,中断整个流的通信

  4. 必须保证所有的消息都处理完成后再关闭,避免数据丢失


三、底层原理篇(深入必问)

1. 为什么 gRPC 选择 HTTP/2 而不是 HTTP/1.1?

HTTP/2 的特性是 gRPC 高性能的核心基础:

  1. 多路复用(Multiplexing):解决了 HTTP/1.1 的队头阻塞问题,单个 TCP 连接可以同时处理多个请求 / 响应,请求和响应可以交错传输,不需要为每个请求新建 TCP 连接

  2. 二进制分帧:把数据拆分为更小的帧,更高效的传输,天然支持流式数据

  3. 头部压缩(HPACK):对 HTTP 头部进行压缩,减少重复头部的传输开销,比如每次请求都带的认证 token、user-agent 这些

  4. 流控与优先级:支持流量控制和流优先级,更好的管理资源

  5. 服务器推送:服务端可以主动向客户端推送资源,支持双向通信

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 三者的关系是什么?

这三者是分层的关系:

  1. HTTP/2:是底层的传输协议,提供了多路复用、流控、二进制分帧这些基础能力

  2. Stream:是 HTTP/2 中的逻辑通道,每个 Stream 对应一个独立的 RPC 调用,多个 Stream 可以复用同一个 TCP 连接

  3. gRPC:是基于 HTTP/2 的 RPC 框架,它把每个 RPC 调用映射到一个 HTTP/2 的 Stream 上,利用 Stream 的双向传输能力实现了四种通信模式

简单来说:gRPC 利用 HTTP/2 的 Stream 机制,实现了 RPC 的调用和流式通信。

4. HTTP/2 有哪些常见的帧类型?各有什么作用?

HTTP/2 以帧为最小传输单位,常见的帧类型:

  1. DATA 帧:传输应用层的业务数据

  2. HEADERS 帧:传输 HTTP 头信息,gRPC 的 Metadata 就是通过这个帧传输的

  3. PRIORITY 帧:设置流的优先级

  4. RST_STREAM 帧:重置流,用于中断流、处理错误

  5. SETTINGS 帧:配置连接的参数,比如窗口大小、最大帧大小

  6. PUSH_PROMISE 帧:服务端推送的预告帧

  7. PING 帧:心跳检测,测量往返时间

  8. GOAWAY 帧:通知对端,连接即将关闭,不要再新建流了

  9. WINDOW_UPDATE 帧:更新流量控制的窗口大小

5. gRPC 的高性能的原因是什么?

这是高频题,总结起来有三点:

  1. 序列化层:Protobuf 二进制序列化,体积小、解析快,比 JSON 高效很多

  2. 传输层:HTTP/2 的多路复用,连接复用,减少 TCP 握手开销,解决队头阻塞

  3. 应用层:强类型契约,编译期生成代码,没有运行时反射开销,异步非阻塞 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 的关系:

  1. 一个 Channel 可以对应多个 TCP Connection

  2. 在客户端负载均衡的模式下,Channel 会管理到多个服务端实例的连接

  3. Channel 是线程安全的,可以被多个 goroutine 并发使用,通常一个服务端的客户端只需要创建一个 Channel 就够了,不需要每次调用都新建

  4. Channel 会自动处理连接的重连、健康检查

4. 什么是 Resolver(解析器)?常见的实现有哪些?

Resolver 是 gRPC 中负责服务发现的组件,它的作用是把服务名称解析成实际的服务端网络地址列表。

常见的 Resolver 实现:

  • DNS Resolver:通过 DNS 解析服务地址

  • etcd Resolver:从 etcd 中获取服务注册的地址,常用在微服务中

  • Consul/Nacos Resolver:从 Consul/Nacos 这些服务注册中心获取地址

  • 自定义 Resolver:开发者可以自己实现解析逻辑,比如从配置中心获取地址

Resolver 会持续监听服务地址的变化,当服务端实例上下线时,自动更新客户端的地址列表,实现服务的动态发现。

5. 什么是 Balancer(负载均衡器)?常见的策略有哪些?

Balancer 是 gRPC 中负责负载均衡的组件,当有多个服务端实例时,它负责选择一个实例来处理客户端的请求。

常见的负载均衡策略:

  1. pick_first:默认策略,选择第一个可用的地址,所有请求都发往这个地址

  2. round_robin:轮询,按顺序把请求分发到不同的实例

  3. weighted_round_robin:加权轮询,根据实例的权重分发请求

  4. least_request:最小请求数,选择当前正在处理的请求最少的实例

  5. p2c(Power of Two Choices):随机选两个实例,选择其中负载更低的那个,go-zero 默认用的就是这个,性能很好

6. 什么是 Deadline/Timeout?它的作用是什么?

Deadline 是 gRPC 中用来设置请求的截止时间的机制,它表示这个请求必须在某个时间点之前完成,否则就会超时返回DEADLINE_EXCEEDED错误。

它的核心作用:

  1. 防止资源泄露:避免请求因为网络问题一直挂起,占用服务端的资源

  2. 防止服务雪崩:当服务端响应慢的时候,客户端不会一直等待,快速失败,避免把服务端压垮

  3. 链路传递:Deadline 会随着 context 在整个微服务调用链中传递,整个调用链共享同一个截止时间,比如网关设置了 3s 的超时,那么下游的所有服务都会自动感知到剩余的时间,不会做无效的处理

注意:Deadline 是绝对时间,不是相对时间,会跨服务传递,所以不需要每个服务都单独设置超时。

7. gRPC 的重试策略有哪些?

gRPC 的重试机制主要有这些策略:

  1. 固定间隔重试:每次失败后,等待固定的时间再重试

  2. 指数退避重试:每次失败后,等待时间指数级增长,比如第一次等 100ms,第二次 200ms,第三次 400ms,避免重试风暴

  3. 错误过滤:不是所有错误都重试,只对可恢复的错误重试,比如Unavailable、DeadlineExceeded,而参数错误、AlreadyExists 这些不可恢复的错误就不重试

  4. 最大重试次数:限制最大重试次数,避免无限重试

8. 什么是 gRPC 的健康检查?

健康检查是 gRPC 提供的标准服务,用来检查服务端的健康状态,客户端或者负载均衡器可以通过这个接口来判断服务是否可用。

健康检查的状态有三种:

  1. SERVING:服务正常,可以处理请求

  2. NOT_SERVING:服务不可用,无法处理请求

  3. UNKNOWN:未知状态,无法确定服务的状态

健康检查可以用来做服务的自动摘除,当服务不健康的时候,负载均衡器会自动把它从可用列表中移除,避免把请求发过去。


五、安全与认证篇(进阶必问)

1. gRPC 支持哪些安全机制?

gRPC 提供了完善的安全机制:

  1. TLS/SSL 加密:对传输的数据进行加密,防止中间人攻击

  2. mTLS(双向 TLS):客户端和服务端互相验证证书,常用于服务间的认证

  3. Token 认证:通过 Metadata 传递 JWT、OAuth2 token,做用户级的认证

  4. 拦截器认证:通过拦截器统一验证 token,实现授权逻辑

2. Channel credentials 和 Call credentials 有什么区别?

这是 gRPC 中两种不同的认证凭证:

  1. Channel credentials:是针对整个连接的凭证,比如 TLS 证书,在建立连接的时候使用,对整个连接的所有 RPC 调用都生效

  2. Call credentials:是针对单个 RPC 调用的凭证,比如 JWT token,每个请求都可以带不同的 token,放在 Metadata 中传输

3. 如何在 gRPC 中实现 JWT 认证?

通常通过拦截器来实现:

  1. 客户端拦截器:在发送请求之前,把 JWT token 添加到 Metadata 中

  2. 服务端拦截器:在请求到达业务方法之前,从 Metadata 中取出 token,验证 token 的有效性,如果验证失败,直接返回未认证的错误,不需要进入业务方法

这样就可以统一处理所有接口的认证,不需要每个业务方法都写认证逻辑。

4. 什么是 mTLS?它和普通 TLS 有什么区别?

普通 TLS 是单向的,只有客户端验证服务端的证书,确认服务端是可信的;而 mTLS(双向 TLS)是客户端和服务端互相验证对方的证书,双方都要确认对方是可信的。

mTLS 常用于服务间的通信,比如微服务架构中,只有拥有合法证书的服务才能互相调用,防止非法的服务接入,提升系统的安全性。


六、服务治理篇(生产必问)

1. gRPC 如何做服务发现?

gRPC 提供了 Resolver 的扩展接口,我们可以集成服务注册中心:

  1. 服务端启动的时候,把自己的地址注册到 etcd/Consul/Nacos 这些注册中心,带上租约

  2. 客户端通过自定义的 Resolver,从注册中心获取服务的地址列表

  3. Resolver 会监听注册中心的变化,当服务端实例上下线的时候,自动更新客户端的地址列表

  4. 客户端的 Balancer 根据更新后的地址列表,做负载均衡

这样就实现了服务的动态发现,不需要手动配置服务端的地址。

2. gRPC 的客户端负载均衡和服务端负载均衡有什么区别?

  1. 客户端负载均衡:客户端自己维护服务端的地址列表,自己选择要调用的实例,不需要中间的负载均衡器,减少了一跳网络,性能更好,比如 gRPC 的内置 Balancer 就是这种

  2. 服务端负载均衡:客户端把请求发给统一的负载均衡器,由负载均衡器选择服务端实例,比如 Nginx、Istio,这种方式对客户端透明,但是多了一跳网络

3. gRPC 中如何实现熔断、限流、降级?

这些都是微服务的服务保护机制,在 gRPC 中通常通过拦截器或者框架来实现:

  1. 限流:通过拦截器,在请求进入的时候,用令牌桶或者计数器算法,限制单位时间的请求数,超过的直接拒绝

  2. 熔断:当某个服务的错误率达到阈值,熔断器打开,后续的请求直接快速失败,避免把下游压垮,等冷却时间后再探测恢复

  3. 降级:当服务压力大的时候,关闭非核心的功能,或者返回默认的缓存数据,保证核心功能可用

比如 go-zero 框架就内置了这些能力,也可以自己通过拦截器实现。

4. gRPC 的重试为什么要配合幂等性?

因为网络超时的时候,我们不确定请求是否已经到达服务端并执行了:

  • 如果请求已经执行成功,只是响应丢了,这时候重试就会导致重复执行,比如重复扣款、重复下单

  • 所以重试必须配合幂等性,保证同一个请求执行多次和执行一次的效果是一样的

常见的幂等实现:

  1. 幂等 Token:客户端生成唯一的请求 ID,服务端用这个 ID 做去重,缓存执行结果

  2. 数据库唯一键:比如订单的请求 ID 加唯一索引,重复插入的时候会捕获错误,返回已有结果

  3. 状态机:通过业务状态来控制,比如订单状态已经是已支付,就不会重复处理


七、版本管理与兼容性篇(架构必问)

1. Proto 文件的字段编号为什么不能随意修改?

因为 Protobuf 序列化的时候,是用字段编号来标识字段的,不是字段名:

  • 如果服务端修改了字段编号,老客户端发送的数据,服务端就会解析到错误的字段上,导致数据错乱

  • 比如老版本中id=1,name=2,服务端改成name=1,id=2,那么老客户端发的 id=1 的数据,服务端就会解析到 name 字段上,完全乱了

正确的版本升级做法:

  1. 只新增字段,不要修改已有字段的编号

  2. 废弃的字段,用reserved关键字保留编号,不要复用

  3. 这样就能保证向后兼容,老客户端可以正常访问新服务,新客户端也可以访问老服务

2. gRPC 服务如何做版本管理?

常见的版本管理方式:

  1. 包版本:把不同版本的服务放在不同的 proto 包中,比如package helloworld.v1和package helloworld.v2,两个版本的服务可以共存,客户端可以选择调用哪个版本

  2. URL 版本:把版本放在服务的地址中,比如不同版本的服务监听不同的端口,或者不同的路径

  3. 字段兼容:对于小的非破坏性变更,通过新增字段来实现,不需要升级版本号,利用 Protobuf 的向后兼容能力

3. Protobuf 如何保证向后兼容?

Protobuf 的设计天然支持向后兼容:

  1. 新增的字段,老客户端会忽略,老服务端也会忽略新客户端发的新字段

  2. 废弃的字段,只要保留编号,就不会影响老客户端

  3. 可以把字段从可选改成重复,或者反过来,只要类型兼容

  4. 只要不修改已有字段的编号和类型,就不会破坏兼容性


八、调试、监控与运维篇(运维必问)

1. gRPC 的二进制不好调试,有什么调试工具?

有很多专用的调试工具:

  1. grpcurl:命令行工具,类似 curl,可以直接调用 gRPC 接口

  2. BloomRPC/Postman/Insomnia:GUI 工具,可以可视化的调用 gRPC 接口,查看请求和响应

  3. gRPC reflection:服务端开启反射服务,客户端就可以动态获取服务的接口定义,不需要 proto 文件,调试工具就是靠这个来工作的

  4. grpc_cli:官方的命令行调试工具

2. gRPC 如何做监控和链路追踪?

  1. Metrics 监控:通过拦截器收集请求的 QPS、延迟、错误率、流量等指标,上报到 Prometheus 等监控系统

  2. 链路追踪:通过拦截器,把 OpenTelemetry/Jaeger/Zipkin 的 Trace 信息放到 Metadata 中,跨服务传递,实现整个调用链的追踪

  3. 日志:通过拦截器统一记录请求的日志,包括方法名、客户端地址、延迟、错误信息等

3. gRPC 的健康检查是如何工作的?

gRPC 提供了标准的Health服务,定义了一个Check方法,客户端可以调用这个方法来检查服务的健康状态:

  • 服务端实现这个方法,返回自己的健康状态

  • 负载均衡器或者服务注册中心会定期调用这个接口,来判断服务是否可用

  • 如果健康检查失败,就会把服务从可用列表中移除

4. gRPC 服务的压测需要关注哪些指标?

压测的时候需要关注:

  1. QPS:服务能承载的最大每秒请求数

  2. 延迟 P99/P999:请求的延迟分布,长尾延迟很重要

  3. 错误率:请求的错误比例

  4. CPU / 内存使用率:服务端的资源使用情况

  5. 连接数:TCP 连接的数量,是否有连接泄露

  6. GC 情况:对于 Go/Java 这些有 GC 的语言,要关注 GC 的停顿时间


九、生产实践与常见问题篇(踩坑必问)

1. gRPC 有什么缺点?

gRPC 不是银弹,它也有缺点:

  1. 调试成本高:二进制格式,不能直接用 curl 调试,需要专用工具

  2. 浏览器不支持:浏览器原生不支持 HTTP/2 的双向流,需要用 gRPC-Web 或者网关转换

  3. 学习成本高:需要学习 Protobuf 的语法,还有 gRPC 的相关概念

  4. Proto 文件管理:多服务的时候,需要管理 proto 文件的版本和依赖

  5. 对老系统不友好:一些老旧的网络设备可能不支持 HTTP/2

2. 什么场景下适合用 gRPC?什么场景下不适合?

适合的场景:

  • 微服务内部的服务间通信,需要高性能

  • 实时通信场景,比如聊天、实时推送、协同编辑

  • 多语言的异构系统,需要跨语言调用

  • 大数据传输场景,比如文件上传下载、批量数据同步

不适合的场景:

  • 对外的简单 API,需要浏览器直接调用的场景

  • 对调试要求很高,需要人类可读的接口的场景

  • 非常简单的小服务,用 REST 就够了,没必要引入 gRPC 的复杂度

3. gRPC 和 Thrift 有什么区别?

两者都是 RPC 框架,区别在于:

  1. 传输协议:gRPC 基于 HTTP/2,可以更好的兼容现有的网络基础设施,而 Thrift 通常用自己的 TCP 协议

  2. 序列化:gRPC 用 Protobuf,Thrift 有自己的序列化格式,性能差不多

  3. 生态:gRPC 的生态更完善,有负载均衡、服务发现、网关、监控这些配套的工具

  4. 流式支持:gRPC 的流式更成熟,基于 HTTP/2 的流,而 Thrift 也有流,但是用的少

4. 常见的 gRPC 错误排查

  1. protoc: command not found:protoc 没有安装,或者没有加到 PATH

  2. Plugin protoc-gen-go not found:Go 的插件没有安装,或者 GOPATH/bin 没有加到 PATH

  3. 连接超时:检查服务端是否启动,端口是否开放,防火墙是否拦截,Docker 的端口映射是否正确

  4. 模块路径错误:导入 pb 包的时候,要替换成自己的 go mod 的模块路径

  5. 流关闭错误:流没有正确关闭,导致资源泄露,要确保用完后关闭流

  6. max message size exceeded:消息太大,超过了默认的 4MB 限制,需要调整max_receive_message_size参数


十、高频面试真题汇总(速查)

  1. Q:gRPC 为什么比 REST 快? A:三个原因:Protobuf 二进制序列化小且快;HTTP/2 多路复用减少连接开销;强类型无反射开销。

  2. Q:gRPC 的四种通信模式是什么? A:一元 RPC、服务端流、客户端流、双向流,分别对应不同的场景。

  3. Q:什么是拦截器?作用是什么? A:中间层组件,用来统一处理认证、日志、监控等逻辑,类似中间件。

  4. Q:HTTP/2 的多路复用解决了什么问题? A:解决了 HTTP/1.1 的队头阻塞,单连接可以并发处理多个请求,提升连接利用率。

  5. Q:Deadline 的作用是什么? A:设置请求的截止时间,防止资源泄露,防止服务雪崩,而且会跨服务链路传递。

  6. Q:Proto 的字段编号为什么不能改? A:因为序列化用编号标识字段,改了会导致新旧版本解析错乱,破坏兼容性。

  7. Q:gRPC 的负载均衡有哪些策略? A:轮询、加权轮询、最小请求、P2C 等。

  8. Q:gRPC 如何做认证? A:TLS 加密、mTLS 双向认证、Token 认证,通过拦截器统一处理。

  9. Q:Metadata 是什么? A:传递请求的元数据,类似 HTTP Header,用来传 token、trace 信息等。

  10. Q:gRPC 的缺点是什么? A:调试难、浏览器不支持、学习成本高、proto 管理成本。