grpc到底是啥! ! !!
一、什么是RPC(Remote Procedure Call)
简单理解:
- RPC是一种让程序可以像调用本地函数一样去调用远程机器上的函数或方法。
- 它的目标:让分布式系统中的不同计算机可以透明地互相通信,实现远程服务调用的封装。
举个例子:
假如你在A电脑上写了个程序,想让远在B电脑的服务器帮你处理某个任务(比如查天气、计算结果)。你不用自己去跟B电脑通信细节,只需要调用RPC,就像调用本地函数一样,RPC会帮你把请求发给远程服务器,等待结果返回。
对RPC不了解的人,或许会纠结其与TCP、HTTP等的关系。后者是网络传输中的协议,而RPC是一种设计、实现框架,通讯协议只是其中一部分,RPC不仅要解决协议通讯的问题,还有序列化与反序列化,以及消息通知。
一个完整的RPC架构里面包含了四个核心的组件,分别是Client ,Server,ClientOptions以及ServerOptions,这个Options就是RPC需要设计实现的东西。
客户端(Client):服务的调用方。
服务端(Server):真正的服务提供方。
客户端存根(ClientOption):socket管理,网络收发包的序列化。
服务端存根(ServerOption):socket管理,提醒server层rpc方法调用,以及网络收发包的序列化。
一、gRPC是什么?
简单总结:
- gRPC是一个开源的高性能远程过程调用(RPC)框架,由谷歌开发。它让不同的服务(可能在不同的机器上)可以像调用本地函数一样相互调用。
- 它支持多种语言(比如C++、Java、Python等),特别适合微服务架构和分布式系统。
关键点:
- 高性能:使用HTTP/2协议,支持多路复用(同时发多个请求)、流控、头压缩,性能优异。
- 跨语言:多语言支持,使得不同平台的服务可以互操作。
- 服务定义:通过.proto文件定义接口,然后自动生成代码(客户端和服务端)。
- 底层数据序列化:用Google的Protobuf(Protocol Buffers)进行高效序列化和反序列化。
二、gRPC的工作流程
你可以想象一个典型的场景:客户端调用远端服务器的一个函数,比如“发送消息”。
1. 定义接口(使用.proto文件)
- 先定义服务接口,例如:
复制代码
service ChatService {rpc SendMessage (MessageRequest) returns (MessageResponse);
}
这里定义了一个SendMessage
的方法。
2. 生成代码
- 使用
protoc
(Protobuf的编译器)将.proto文件编译成不同语言的代码(比如C++的客户端和服务端代码)。
3. 客户端调用
- 客户端调用由gRPC自动生成的“stub”对象,就像调用本地函数一样。
- 客户端传入参数(比如消息内容),gRPC会把请求序列化(用Protobuf压缩成二进制格式),通过HTTP/2协议发往服务器。
4. 服务器端处理
- 服务器收到请求后,拆包(反序列化)出参数,调用对应的逻辑,然后生成响应(比如“已发消息”),再序列化发送回客户端。
5. 客户端收到响应
- 客户端反序列化得到响应内容。
三、问答:.proto文件到底干啥的?为什么要用它?
这是个关键问题!让我们详细讲讲。
1. .proto
文件的作用
- 它定义了“接口”和“数据结构”。就像“合同”一样,规定了客户端和服务器端要怎么交流。
- 它声明了“服务方法”——比如说
SendMessage
;同时还定义了这个方法的参数和返回值的结构。
2. 具体内容
- 接口定义:告诉gRPC“我提供哪些服务”。
- 消息定义:定义了请求和响应的数据结构,比如:
复制代码
message MessageRequest {string sender = 1;string content = 2;
}message MessageResponse {bool success = 1;string message = 2;
}
- 这样,客户端知道要发什么(sender和content),服务器知道怎么理解这些数据。
3. 作用总结
- 自动生成代码:
protoc
工具会根据.proto
文件生成功能完整的客户端和服务器端代码(封装了序列化、网络传输等细节)。 - 保证一致性:定义一次,看两边都用,不会因为实现而不一致。
四、gRPC的技术细节(深入一些)
方面 | 内容 |
---|---|
底层协议 | HTTP/2,支持多路复用、流控、头压缩,提高效率。 |
序列化 | 使用Protobuf,将结构化数据压缩成二进制,传输快,占用空间少。 |
数据定义 | .proto 文件定义服务接口和数据结构,编译生成各语言代码。 |
代码生成 | 通过protoc 工具,支持多语言(C++、Java、Python、Go等)的客户端和服务端代码。 |
流式传输 | 支持单向流和双向流(比如聊天中的实时消息推送),更灵活。 |
异步调用 | 支持异步非阻塞调用,提高并发性能。 |
路由和负载均衡 | 支持网关、负载均衡方案,适合大规模微服务架构。 |
五、你的项目中的应用
在你的C++即时通讯项目中,gRPC可以帮助你:
- 定义消息和接口(比如发消息、接收消息、加入房间、退出房间等)
- 简化客户端和服务端的调用逻辑
- 提升性能(HTTP/2带来的优势)
- 通过.proto文件保持接口一致,方便维护和扩展
六、总结
.proto
文件是定义你的通信合同(接口和数据结构)的“蓝图”。- 你用
protoc
编译后,会得到自动生成的代码,这些代码封装了请求的序列化、网络传输、反序列化。 - gRPC让你像调用本地函数一样,方便、快速地在网络上调用远端服务,支持多路复用和流式通信,非常适合高性能分布式系统。
- .proto里规定了客户端要请求什么样的消息,服务端要回什么样的消息,用protoc编译后,就分别自动生成了客户端和服务端的代码,里面包含了各自发送,接收数据的要求,还有序列化和反序列化方法,以及网络传输(http/2)
一、先搞懂核心三件套:gRPC = Protobuf + HTTP/2 + 强类型服务
想象你要在两个程序之间传递数据,需要解决三个问题:
-
数据怎么打包?(Protobuf序列化)
-
数据怎么传输?(HTTP/2协议)
-
远程方法怎么调用?(服务端/客户端代码自动生成)
而.proto
文件就是这三个问题的统一解决方案模板!
二、.proto文件详解(即时通讯场景举例)
假设你的IM项目需要实现「登录」和「发送消息」功能,对应的.proto
文件长这样:
protobuf
复制
下载
// 定义消息结构(类似C++的struct) message LoginRequest {string username = 1; // 字段编号(不是值!)string password = 2; }message LoginResponse {bool success = 1;string token = 2; }message ChatMessage {string from_user = 1;string to_user = 2;string content = 3;int64 timestamp = 4; }// 定义服务接口(类似C++的虚类) service IMService {// 一元RPC(一发一收)rpc Login(LoginRequest) returns (LoginResponse);// 服务端流式RPC(适合消息推送)rpc ReceiveMessages(LoginResponse) returns (stream ChatMessage);// 客户端流式RPC(适合批量上传)rpc SendMessages(stream ChatMessage) returns (ResponseStatus);// 双向流式RPC(适合实时聊天室)rpc ChatRoom(stream ChatMessage) returns (stream ChatMessage); }
关键解读:
-
消息定义:相当于网络传输的「信封格式」,Protobuf会根据它生成C++类
-
服务定义:声明了远程调用的方法签名,gRPC会根据它生成客户端和服务端骨架代码
-
流式设计:
stream
关键字表示持续传输(非常适合即时通讯场景)
三、gRPC工作全流程(以发送消息为例)
假设用户A(客户端)发送消息给用户B(服务端):
-
客户端侧:
-
构造
ChatMessage
对象并填充数据 -
调用自动生成的
stub->SendMessages()
方法 -
gRPC用Protobuf将对象序列化成二进制(体积比JSON小3-5倍!)
-
通过HTTP/2协议发送(支持多路复用、头部压缩)
-
-
服务端侧:
-
接收二进制数据,用相同的
.proto
文件反序列化成ChatMessage
-
调用你实现的
SendMessages
业务逻辑(比如存数据库、转发给用户B) -
将响应序列化后通过HTTP/2返回
-
四、为什么用HTTP/2而不是HTTP/1.1?
特性 | HTTP/1.1 | HTTP/2 | 对IM项目的意义 |
---|---|---|---|
连接方式 | 多个TCP连接 | 单个TCP连接多路复用 | 减少连接数,适合高并发IM |
数据传输 | 文本格式 | 二进制帧 | 更高效,适合Protobuf |
头部信息 | 每次重复发送 | HPACK压缩 | 节省带宽,尤其对小消息重要 |
服务器推送 | 不支持 | 支持 | 可实现服务端主动推送消息 |
五、4种通信模式(重点!)
1. 一元RPC(Unary)
-
模式:类似普通函数调用 → 客户端发1个请求,服务端回1个响应
-
IM场景:登录认证、单条消息ACK
2. 服务端流式(Server streaming)
-
模式:客户端发1个请求,服务端返回流式响应
-
IM场景:用户上线后持续接收新消息(类似WebSocket)
3. 客户端流式(Client streaming)
-
模式:客户端流式发送多个请求,服务端返回1个响应
-
IM场景:批量上传聊天记录
4. 双向流式(Bidirectional streaming)
-
模式:客户端和服务端同时流式收发
-
IM场景:实时聊天室、语音通话信令
六、关键细节(C++实现注意)
1. 线程模型
-
gRPC默认创建多个线程:
-
1个线程处理网络I/O
-
多个线程执行CompletionQueue(建议根据CPU核心数配置)
-
-
优化建议:对于高并发IM,使用异步接口(Async API)
2. 元数据(Metadata)
-
类似HTTP头部,用于传递额外信息:
cpp
// 客户端添加元数据 ClientContext context; context.AddMetadata("user-agent", "im-client/1.0");// 服务端读取元数据 auto headers = context.client_metadata(); auto it = headers.find("user-agent"); if (it != headers.end()) {std::string user_agent(it->second.data(), it->second.size()); }
3. 截止时间(Deadline)
cpp
复制
下载
ClientContext context; auto deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(100); context.set_deadline(deadline);
4. 错误处理
cpp
复制
下载
Status status = stub_->Login(&context, request, &response); if (!status.ok()) {if (status.error_code() == StatusCode::DEADLINE_EXCEEDED) {// 处理超时} else if (status.error_code() == StatusCode::UNAVAILABLE) {// 服务不可用} }
七、性能优化技巧(针对IM场景)
-
复用Stub和Channel:创建Channel成本高,不要每次调用都新建
-
使用流式接口:相比多次Unary调用,流式能减少TCP握手开销
-
调整HTTP/2参数:
cpp
复制
下载
ChannelArguments args; args.SetInt(GRPC_ARG_HTTP2_BDP_PROBE, 1); // 启用带宽探测 args.SetInt(GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_MS, 5000); // 心跳间隔 auto channel = CreateCustomChannel(server_address, InsecureChannelCredentials(), args);
-
启用压缩:
cpp
复制
下载
// 在ClientContext中设置 context.set_compression_algorithm(GRPC_COMPRESS_GZIP);
八、为什么选择gRPC而不是裸Socket?
对比项 | 裸Socket | gRPC |
---|---|---|
协议设计 | 需要自定义协议 | 内置Protobuf+HTTP/2 |
跨语言支持 | 需为每种语言实现 | 自动生成多语言代码 |
流控机制 | 需手动实现 | 内置基于HTTP/2的流控 |
生产级特性 | 需自实现重试/加密等 | 内置负载均衡、重试等 |
开发效率 | 低(造轮子) | 高(专注业务逻辑) |
九、你的项目该用哪种模式?
根据你的IM需求:
-
用户登录 → 一元RPC
-
消息接收 → 服务端流式(长连接推送)
-
群聊消息 → 双向流式(类似聊天室)
-
文件传输 → 客户端流式(分块上传)
十、终极总结
-
.proto文件 → 定义通信契约(接口+数据结构)
-
Protobuf → 高效编解码器(省带宽、快解析)
-
HTTP/2 → 高性能传输层(多路复用、流控)
-
代码生成 → 免去手写网络代码(客户端stub+服务端虚类)
建议在你的C++项目中:
-
先设计好
.proto
接口 -
用
protoc
生成代码 -
实现服务端业务逻辑
-
客户端直接调用生成的API
遇到具体问题时可以再深入某个模块(比如流式处理或性能优化)!
1. gRPC的概览
- 核心功能:gRPC是一个基于HTTP/2协议的远程过程调用(RPC)框架,提供高性能、跨语言的服务通信解决方案。
- 重点:
- 自动生成代码(用.proto文件定义接口)
- 支持多种语言(C++, Java, Python 等)
- 内置流式、双向流、超时等丰富特性
- 使用HTTP/2作为底层传输协议,保证高效传输
2. Boost.Asio的概览
- 核心功能:Boost.Asio是一个C++的异步I/O库,用于网络编程和异步操作,提供底层网络通信和事件驱动模型。
- 重点:
- 纯粹是网络编程工具箱
- 提供TCP、UDP、串口、定时器等各种底层接口
- 灵活、轻量,适合手动实现各种协议和通信逻辑
3. 关系和区别
-
关系:
- gRPC可以基于Boost.Asio:在底层实现中,gRPC的C++版本其实在某些实现(比如gRPC C++)中可能会用到某些异步I/O机制,但它们的关系并不是直接嵌套。
- gRPC的传输层依赖HTTP/2协议,后端可以借助底层库(比如nghttp2或 svoa 来实现HTTP/2通信),而核心的网络操作可以抽象化隐藏,不需要你手动操控。
- Boost.Asio可以用来实现自定义的通信协议,比如自己封装协议、实现底层SSL/TLS等功能,但它不是专门为RPC设计的。
-
区别:
- gRPC是一套高层的RPC框架,帮你省去协议设计、消息序列化、连接管理等繁琐工作。
- Boost.Asio是底层的异步网络库,你可以用它自己从零实现任何协议或框架,包括RPC,但工作量要大得多。
4. 总结
- 如果你用gRPC, 你主要关注定义接口、调用逻辑,gRPC帮你打理底层的HTTP/2和序列化等细节,它可以内部可能会用到类似Boost.Asio的异步I/O(具体实现依赖的底层库不同)。
- 如果你用Boost.Asio, 你会自己手动实现协议和通信逻辑,灵活性更大,但需要写更多底层代码。
举个比喻:
- gRPC像是一辆自动驾驶的豪华车,帮你快速、安全、省心地跑远路。
- Boost.Asio像是一个强大的引擎,你可以用它自己打造各种机械装置,从零起步,定制适合你需求的“车辆”。
如果你想在C++中搭建服务,想用简单点、效率高点的RPC方案,那么gRPC是个好选择,当然,也可以自己用Boost.Asio从头打造你的“专属”协议。
那么,gRPC和Boost.Asio到底有啥关系呢?我们可以分几步说明:
1. 它们的角色不同,但可以相互关联:
-
gRPC:关心“我有什么要用的接口、调用远程服务、消息怎么传递”,它帮你做好了定义和组织工作。
-
Boost.Asio:关心“我怎么用异步操作来实现网络连接、数据传输”,它帮你搞定底层的网络细节。
关系:
gRPC在实现过程中,可能会用到类似Boost.Asio这样的底层工具库(特别是在C++版本)。就是说,gRPC的某些部分可能会依赖Boost.Asio或类似的库来实现网络连接(比如打开一个连接、读写数据等)。
2. 它们的工作流程(通俗版):
想象一下你在用一个高层次的服务(gRPC)调用另一个服务,但实现这个服务的底层网络连接,可能需要用到很底层的“工具” — 这时候就会用到Boost.Asio。
比如:
- 你用gRPC定义了一个“计算服务”
- 后端想实现这个“计算服务”,需要建立网络连接、传输数据、处理请求
- 实现这些底层网络传输,可以用Boost.Asio,像拼积木一样搭建起一个专门的网络架构
- 这样,gRPC用自己的方式(封装、序列化、协议)管理调用,但底层的连接和数据传输由Boost.Asio处理。
3. 让人更容易理解的比喻:
你想送快递(gRPC):你只需要把东西放到快递箱,填好地址,然后告诉快递公司(gRPC)去寄。
快递公司当中用的工具(底层技术,比如Boost.Asio):在送快递的过程中,会用到很多车、司机、路标、装卸工,确保快递安全到达。
在这个比喻里:
- 你(开发者)只关心快递内容和地址(定义RPC接口)。
- 快递公司(gRPC)负责组织寄送流程。
- **底层的交通工具(Boost.Asio)**则是用来实际建立连接、传输数据的硬件和技巧。
4. 具体一点点讲:
-
gRPC用来定义你的通信协议和接口,它负责把“调用”和“结果”打包(序列化),以及管理连接,确保数据的正确性。
-
Boost.Asio(或类似的工具)在底层帮忙实现连接的建立、断开,数据的异步传输,防止阻塞,保证效率。
总结:
- 关系: gRPC 后端的某些实现可能会用到 Boost.Asio 这种底层网络库来搞定连接和数据传输。
- 不同点: gRPC是面向开发者的高层框架,帮你定义接口和管理调用,而Boost.Asio是底层的工具库,用于构建这些连接。
三、关键设计对比
特性 | gRPC | Boost.Asio |
---|---|---|
协议支持 | 强制使用HTTP/2 + Protobuf | 支持任意协议(TCP/UDP/自定义) |
线程模型 | 内置多线程池管理 | 需手动管理io_service 多线程 |
数据序列化 | 强制使用Protobuf | 无限制(可自定义格式) |
适用场景 | 微服务通信、跨语言调用 | 单机高性能网络编程 |
复杂度 | 高层封装,开箱即用 | 底层控制,需自行实现业务逻辑 |
四、协作可能性
1. 混合使用场景
-
案例1:用gRPC实现服务间通信,同时用Boost.Asio处理边缘设备的自定义协议。
-
案例2:在gRPC服务内部,使用Boost.Asio管理本地高性能计算任务(如文件异步读写)38。
2. 性能优化互补
-
gRPC的瓶颈:若HTTP/2协议栈成为性能瓶颈(如超高频小包),可改用Boost.Asio实现裸TCP优化。
-
Boost.Asio的局限:若需跨语言支持或服务治理(如负载均衡),可迁移到gRPC69。
五、通俗示例:即时通讯项目中的分工
假设你开发一个分布式IM系统:
-
gRPC负责:
-
用户登录认证(跨服务调用)
-
消息存储到数据库(微服务间通信)
-
跨数据中心消息同步(通过HTTP/2多路复用)
-
-
Boost.Asio负责:
-
实时聊天室的长连接管理(自定义TCP协议)
-
文件分片传输(异步I/O优化)
-
心跳包检测(精确控制超时逻辑)17
-
六、选择建议
-
用gRPC:需要快速实现标准化服务、跨语言支持、流式通信(如你的IM项目中的服务间调用)。
-
用Boost.Asio:需要极致性能、自定义协议、或与硬件设备交互(如物联网网关)69。
七、底层原理关联
1. 异步模型对比
-
gRPC:基于HTTP/2的流式多路复用,本质是Reactor模式(事件驱动)。
-
Boost.Asio:在Windows使用Proactor模式(异步I/O完成端口),在Linux用epoll模拟Proactor47。
2. 多线程处理
-
gRPC:自动管理线程池,开发者无需关注细节。
-
Boost.Asio:需手动创建线程调用
io_service::run()
,并通过strand
保证线程安全17。
八、性能数据参考
-
gRPC:单机可达10万+ QPS(依赖Protobuf高效序列化)。
-
Boost.Asio:优化后可达100万+ QPS(裸TCP场景)6。
-
取舍:gRPC牺牲部分性能换取易用性,Boost.Asio以复杂度换取极致性能56。
总结
-
关系本质:gRPC和Boost.Asio是不同层级的工具,前者解决服务间通信的标准化问题,后者解决单机网络编程的性能问题。
-
协作价值:在复杂系统中,可组合使用——用gRPC实现主体架构,用Boost.Asio优化关键路径。