面试官:讲讲 gRPC?别慌,老码小张带你从原理到实践彻底搞懂它!

咱们平时写代码,是不是经常遇到服务之间互相调用的场景?比如订单服务要调库存服务查库存,用户服务要去权限中心验权限。最常见的就是用 RESTful API + JSON 来搞定,对吧?写起来是挺方便,浏览器也能直接访问。

但你想过没,如果内部服务之间调用特别频繁,数据量还贼大,每次都用 JSON 传来传去,序列化、反序列化是不是有点慢?而且 REST API 的接口定义,有时候靠文档约定,时间长了、人一多,是不是容易对不齐,甚至"接口打架"?

这时候你可能会想:有没有一种更快、更规范、更适合服务内部通信的技术呢?

Bingo!今天咱们就来聊聊 Google 出品的 gRPC,看看它是怎么解决这些问题的,以及为啥现在越来越火。搞懂了它,下次再遇到类似场景,或者面试官问起,你就能胸有成竹了!

gRPC 到底是个啥?

简单来说,gRPC 就是 Google 搞出来的一套高性能、开源、通用的 RPC(远程过程调用)框架。啥叫 RPC?就是让你调用远程服务器上的方法,感觉就像调用本地方法一样丝滑,不用关心底层的网络细节。

gRPC 有几个特别亮眼的特点:

  1. 快! 这是它的核心卖点之一。至于为啥快,咱们后面细聊。
  2. 跨语言! 你可以用 Java 写服务端,用 Go 写客户端,或者 Python、Node.js、C++... 随便组合,都没问题。
  3. 基于接口定义! 它强制你先定义好服务接口和数据结构,然后自动生成代码,保证了规范性。

听起来是不是有点意思?那它是怎么做到这些的呢?咱们一层层剥开看。

gRPC 的三大法宝

gRPC 的高性能和规范性,主要靠下面这三样东西撑起来:

1. ProtoBuf:不只是序列化,更是"契约"

咱们先说说数据怎么传输。REST API 常用的 JSON 是文本格式,可读性好,但体积大,解析慢。gRPC 则默认使用 Protocol Buffers (ProtoBuf)

ProtoBuf 也是 Google 出品的,是一种语言无关、平台无关、可扩展的序列化机制。你可以把它想象成一种更紧凑、更快的 JSON 或 XML。它把数据序列化成二进制格式,体积小得多,解析速度自然也快很多。

但 ProtoBuf 不仅仅是序列化格式,它更重要的作用是 IDL (Interface Definition Language),也就是接口定义语言。

你需要在一个 .proto 文件里,像写代码一样,先定义好你的服务(有哪些方法),以及这些方法需要传输的数据结构(消息体)。

比如,咱们定义一个简单的打招呼服务:

protobuf 复制代码
// 指定 proto 版本
syntax = "proto3";

// 定义包名,避免命名冲突
package greet;

// 定义服务
service Greeter {
  // 定义一个简单 RPC 方法,接收 HelloRequest,返回 HelloReply
  rpc SayHello (HelloRequest) returns (HelloReply);
}

// 定义请求消息体
message HelloRequest {
  string name = 1; // 1 是字段编号,不是值
}

// 定义响应消息体
message HelloReply {
  string message = 1;
}

看到了吗?用 .proto 文件,我们把服务接口 Greeter、方法 SayHello、请求参数 HelloRequest 和返回结果 HelloReply 都规定得死死的。这份 .proto 文件就像一份技术合同,服务端和客户端都得遵守。

然后,gRPC 提供了工具,可以根据这份 .proto 文件,自动生成各个语言的服务端骨架代码和客户端 Stub(桩代码)。你只需要实现服务端的业务逻辑,以及在客户端直接调用生成好的方法就行了,省去了大量手写网络请求、序列化反序列化的模板代码,还保证了类型安全,不容易出错。

2. HTTP/2:高性能的秘密武器

光有高效的 ProtoBuf 还不够,数据得在网络上传输。gRPC 没有选择广泛使用的 HTTP/1.1,而是直接构建在 HTTP/2 之上。

为啥是 HTTP/2?因为它相比 HTTP/1.1 有质的飞跃,完美契合了 gRPC 对高性能的需求:

  • 多路复用 (Multiplexing): 这是 HTTP/2 最牛逼的改进之一。HTTP/1.1 有"队头阻塞"问题,一个请求没回来,后面的就得等着。而 HTTP/2 允许在一个 TCP 连接上同时发送和接收多个请求/响应,互不干扰。这大大提高了连接利用率和并发性能,尤其适合 RPC 这种频繁调用的场景。

    你可以想象一下:

    • HTTP/1.1 就像单车道,一次只能过一辆车,堵了就全堵了。
    • HTTP/2 像是多车道高速公路,多辆车可以并行跑,效率高多了。
    sequenceDiagram participant Client participant Server Note over Client,Server: HTTP/1.1 (单个连接,串行处理) Client->>Server: Request 1 Server-->>Client: Response 1 Client->>Server: Request 2 Server-->>Client: Response 2 Note over Client,Server: HTTP/2 (单个连接,多路复用) Client->>Server: Request A (Stream 1) Client->>Server: Request B (Stream 3) Server-->>Client: Response A (Stream 1) Server-->>Client: Response B (Stream 3) Client->>Server: Request C (Stream 5) Server-->>Client: Response C (Stream 5)
  • 二进制分帧 (Binary Framing): HTTP/1.1 是纯文本的,解析起来费劲。HTTP/2 把所有传输的信息(HTTP 头、Body)都分割成更小的二进制帧,解析更高效、更健壮。ProtoBuf 本身就是二进制的,和 HTTP/2 的二进制分帧简直是天作之合。

  • 头部压缩 (Header Compression - HPACK): REST API 每次请求,HTTP Headers (像 User-Agent, Accept 等) 都得重复发送一遍,挺浪费带宽的。HTTP/2 使用 HPACK 算法,可以有效压缩头部信息,减少传输的数据量,尤其在请求频繁时效果显著。

  • 服务端推送 (Server Push): (这个 gRPC 用得相对少,但也是 HTTP/2 的特性)服务端可以主动向客户端推送资源,比如网页请求 HTML 后,服务器主动把 CSS、JS 推过来,减少客户端等待。

正是因为有了 HTTP/2 这个强大的基座,gRPC 才能实现那么高的传输效率。

3. 四种通信模式:灵活应对各种场景

除了基本的"请求-响应"模式,gRPC 还基于 HTTP/2 的流式传输能力,提供了四种通信模式,让交互更灵活:

  1. Unary RPC (一元 RPC): 最简单的模式,客户端发一个请求,服务端回一个响应。就像普通的函数调用。咱们上面那个 SayHello 例子就是。
  2. Server streaming RPC (服务端流式 RPC): 客户端发一个请求,服务端返回一个数据流。就像订阅了服务器的消息,服务器会不断地给你推送信息。比如,客户端请求获取某个商品的实时价格更新,服务端就可以源源不断地把价格变动推给客户端。
  3. Client streaming RPC (客户端流式 RPC): 客户端发送一个数据流,服务端返回一个响应。比如,客户端需要上传一个大文件,可以分成很多块,边读边传给服务端,传完后服务端给一个确认响应。
  4. Bidirectional streaming RPC (双向流式 RPC): 客户端和服务端都可以向对方发送数据流。就像打电话一样,两边可以同时说话。非常适合做实时聊天、交互式应用等场景。

这四种模式覆盖了绝大多数的服务间通信需求,比传统的 REST API(基本只有 Unary 模式)要灵活得多。

gRPC vs. REST API:简单对比

说了这么多 gRPC 的好处,那是不是 REST API 就没用了呢?当然不是。它们各有优劣,适用于不同的场景。咱们简单对比下:

特性 gRPC REST API
底层协议 HTTP/2 通常是 HTTP/1.1 (也可用于 HTTP/2)
数据格式 Protocol Buffers (默认,二进制) JSON (最常用,文本), XML, etc.
接口定义 .proto 文件 (IDL), 强类型,代码生成 OpenAPI/Swagger (可选), 相对松散
性能 高 (二进制序列化, HTTP/2) 相对较低 (文本序列化, HTTP/1.1 限制)
浏览器支持 有限 (需要网关/代理,如 gRPC-Web) 非常好 (浏览器原生支持)
流式传输 支持四种模式 (Unary, Server/Client/Bi-di) 基本只支持 Unary (可用 WebSocket 等补充)
代码生成 强大,跨语言支持好 依赖 OpenAPI 工具,支持程度不一
主要场景 内部微服务通信,性能敏感场景 对外开放 API, Web 应用,简单 CRUD

简单来说:

  • 如果你在搞内部微服务 ,追求高性能强一致性跨语言开发效率,gRPC 通常是更好的选择。
  • 如果你的 API 需要直接暴露给浏览器 ,或者对可读性要求很高,或者团队对 REST 更熟悉,那么 REST API 依然是主流且合适的选择。

实践中的一点干货

光说不练假把式。真正在项目里用 gRPC,还有些事儿得注意:

  1. 错误处理: gRPC 通过状态码(Status Code)和可选的状态消息(Status Message)来传递错误。你需要定义好业务错误码,并通过 metadata 来传递更详细的错误信息。
  2. Metadata: 有点像 HTTP Header,可以用来传递一些请求/响应之外的元数据,比如身份认证 Token、追踪 ID (Tracing ID)、自定义业务标记等。
  3. 负载均衡: gRPC 客户端可以实现复杂的负载均衡策略(比如轮询、加权轮询),或者结合服务发现(如 Consul, Etcd)来做。也可以用 Linkerd、Istio 这样的服务网格(Service Mesh)来提供更强大的流量管理能力。
  4. 测试: 可以用 grpcurl 这个命令行工具来测试 gRPC 服务,类似 curl 之于 HTTP API。也有一些 GUI 工具,比如 Postman 现在也支持 gRPC 了,还有 BloomRPC 等。
  5. gRPC-Web: 如果你想让浏览器直接调用 gRPC 服务,需要一个中间层,比如 Envoy 代理或者使用 gRPC-Web 库,它会将 gRPC 请求转换为浏览器友好的 HTTP/1.1 请求。
  6. 版本管理: .proto 文件是接口定义,它的变更需要小心管理,尤其是要考虑向后兼容性,避免破坏现有的客户端。ProtoBuf 本身提供了一些字段演进的规则。

结语

好啦,关于 gRPC 的核心原理和一些实践要点,今天就先跟大家聊到这儿。希望能帮你理解为啥 gRPC 那么快(ProtoBuf + HTTP/2),以及它强在哪儿(IDL + 代码生成 + 多种通信模式)。

它不是万能药,也不能完全取代 REST,但在微服务架构的内部通信场景下,gRPC 确实提供了一个非常高效、规范的选择。下次做技术选型或者优化服务性能时,不妨考虑一下它。


我是老码小张,一个喜欢琢磨技术背后原理,又在实践中不断踩坑、不断成长的普通技术人。如果你觉得这篇文章对你有帮助,或者有啥不同看法,欢迎在评论区留言交流,咱们一起进步!

相关推荐
Victor356几秒前
Netty(13)Netty中的事件和回调机制
后端
神仙别闹9 分钟前
基于C语言实现B树存储的图书管理系统
c语言·前端·b树
鹏说大数据25 分钟前
数据治理项目实战系列6-数据治理架构设计实战,流程 + 工具双架构拆解
大数据·数据库·架构
玄魂28 分钟前
如何查看、生成 github 开源项目star 图表
前端·开源·echarts
一水鉴天29 分钟前
整体设计 定稿 之26 重构和改造现有程序结构 之2 (codebuddy)
开发语言·人工智能·重构·架构
隐语SecretFlow1 小时前
【隐语Secreflow】如何配置 Kuscia 对请求进行 Path Rewrit
架构·开源
码事漫谈1 小时前
VS Code 1.107 更新:多智能体协同与开发体验升级
后端
小二·1 小时前
MyBatis基础入门《十四》多租户架构实战:基于 MyBatis 实现 SaaS 系统的动态数据隔离
数据库·架构·mybatis
码事漫谈1 小时前
从概念开始开始C++管道编程
后端
@淡 定1 小时前
Spring中@Autowired注解的实现原理
java·后端·spring