RPC 介绍
核心思想:
让调用远程服务像调用本地函数一样简单
1 2 3 4
| int result = local_add(1, 2);
int result = remote_add(1, 2);
|
解决了分布式系统中的服务间通信,同时隐藏了网络通信的复杂性,提供了类型安全的远程调用
RPC 基本组件
一个完整的 RPC 系统通常包含以下三个层次:
- 接口定义语言(IDL, Interface Definition Language)
—— 描述服务方法和数据结构。
- 数据序列化格式
—— 将结构化数据转换为可传输的字节流。
- 网络通信协议
—— 负责请求/响应的传输、连接管理、错误处理等。
protobuf 能解决前两个问题,比如
1 2 3 4 5 6
| service UserService { rpc GetUser(GetUserReq) returns (UserRsp); rpc CreateUser(CreateUserReq) returns (UserRsp); } message GetUserReq { int32 user_id = 1; }
|
1 2 3 4 5 6 7 8
| GetUserReq request; request.set_user_id(123); std::string serialized_data = request.SerializeAsString();
GetUserReq new_request; new_request.ParseFromString(serialized_data);
|
而 rpc 框架通常能自己解决第三层网络协议
第一层:接口描述文件层(IDL Layer),通常用 .proto 来定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| syntax = "proto3";
service UserService { rpc GetUser(GetUserRequest) returns (UserResponse); rpc CreateUser(CreateUserRequest) returns (UserResponse); }
message GetUserRequest { int32 user_id = 1; }
message UserResponse { int32 user_id = 1; string name = 2; string email = 3; }
|
第二层:RPC 协议层(Protocol Layer),通常由 protoc.exe配合 RPC 的插件使用,生成的是比如 .grpc.pb.cc 和 .grpc.pb.h文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class UserService_Stub : public ::google::protobuf::Service { public: void GetUser(::google::protobuf::RpcController* controller, const GetUserRequest* request, UserResponse* response, ::google::protobuf::Closure* done); virtual void GetUser(::google::protobuf::RpcController* controller, const GetUserRequest* request, UserResponse* response, ::google::protobuf::Closure* done) = 0; };
|
第三层:网络通信层(Transport Layer),这一层通常自己实现
1 2 3 4 5 6 7 8 9 10 11 12 13
| class GrpcTransport { public: bool Connect(const std::string& endpoint); bool SendRequest(const std::string& method_name, const std::string& serialized_data); std::string ReceiveResponse(); void HandleErrors(); };
|
使用 grpc + protobuf 的完整流程
Protocol Buffers 是一种接口定义语言(IDL)和高效的序列化框架,它提供了跨语言的数据结构和服务接口定义能力。
1 2 3
| Protocol Buffers = ├── 接口定义语言 (IDL) └── 序列化框架
|
gRPC 是基于 Protobuf 构建的一个高性能 RPC 框架,基于 HTTP/2 协议构建,但不仅仅 “协议” 本身,而是一个 “面向服务的通信框架”。专为微服务设计。
| 层级 |
技术 |
| 传输层 |
TCP |
| 应用层协议 |
HTTP/2 |
| 序列化格式 |
Protocol Buffers |
| RPC 框架 |
gRPC(封装了上述三层) |
而 Protobuf 本身还可以作为许多其他系统(如消息队列、API 网关、配置管理等)的基础数据定义格式,基础用法如下:
1 2 3
| 1. 用 .proto 定义接口以后, 2. 使用 .protoc 配合对应框架的插件直接生成代码 3. 将代码代码添加进项目中,在项目中直接使用生成的类。
|
步骤 1:编写 .proto 文件(定义服务契约)
首先序列化生成数据结构(.proto文件不依赖任何特定编程语言),通常写作:
1 2 3 4 5 6 7 8 9
| syntax = "proto3"; package example;
service Calculator { rpc Add(AddRequest) returns (AddResponse); }
message AddRequest { int32 a = 1; int32 b = 2; } message AddResponse { int32 result = 1; }
|
步骤 2:用 protoc 生成代码
1
| protoc --cpp_out=. calculator.proto -> calculator.pb.h, calculator.pb.cc
|
再利用插件生成 grpc 服务框架(插件保证了 protobuf 可以为不同用途生成不同代码)
1 2
| protoc --grpc_out=. --plugin=protoc-gen-grpc=grpc_cpp_plugin.exe calculator.proto -> calculator.grpc.pb.h, calculator.grpc.pb.cc
|
输出文件:
calculator.pb.h/cc → 数据结构
calculator.grpc.pb.h/cc →服务接口(Stub + Service)
步骤 3 :在项目中使用生成代码:
客户端:通过 Stub 发起远程调用;
服务端:继承 Service 类并实现具体方法;