Protobuf文件必须首行声明syntax="proto3",gRPC C++仅支持proto3;字段默认optional,枚举首值须为0;需统一protoc版本生成代码,服务端用BuildAndStart()启动,客户端高并发应使用异步API与复用CompletionQueue。

Protobuf 文件定义必须显式指定 syntax = "proto3"
很多初学者在写 .proto 文件时漏掉语法声明,导致 protoc 编译失败并报错 Expected "syntax = ..." 。gRPC C++ 默认只支持 proto3,且不兼容 proto2 的默认值、required 字段等行为。
-
syntax = "proto3";必须是文件第一行(可空行,但不可注释) - 所有字段默认为 optional,无需加
optional关键字(加了会报错) - 枚举第一个值必须为 0,否则反序列化时可能被当为未设置
- 使用
google/protobuf/wrappers.proto中的Int32Value等才能表达“空值”语义
syntax = "proto3";
package example;
message Request {
string user_id = 1;
int32 timeout_ms = 2;
}
message Response {
bool success = 1;
string message = 2;
}
service Greeter {
rpc SayHello(Request) returns (Response);
}
C++ 客户端和服务端代码必须用 protoc 生成相同的头文件和源文件
手动改写或复用旧版生成代码极易引发 ABI 不匹配:比如服务端用 protobuf 3.21 编译,客户端用 3.19 生成的 *.grpc.pb.h,会导致 Unknown field in serialization 或崩溃。
- 始终用同一版本
protoc(推荐与libprotobuf-dev同版本)执行生成 - 生成命令必须包含
--grpc_out和--cpp_out,且顺序不能颠倒 - 编译时需同时链接
libgrpc++、libprotobuf、libgrpc—— 少一个就链接失败或运行时undefined symbol - 确保
#include路径包含生成目录,且优先级高于系统 protobuf 头(避免混用)
服务端启动必须调用 ServerBuilder::BuildAndStart(),不能只调用 Build()
常见错误是只调用 builder.Build(),结果程序无报错但监听端口不生效、客户端连接超时。这是因为 Build() 只返回 std::unique_ptr,但没真正启动 I/O 循环。
-
BuildAndStart()内部会启动 gRPC 的 completion queue 线程池,默认线程数为 CPU 核心数 - 若需控制并发,应通过
SetMaxMessageSize()、SetResourceQuota()限制,而非减少线程数 - 服务端 shutdown 必须先调用
server->Shutdown(),再wait_for_idle(),否则可能丢弃正在处理的请求
grpc::ServerBuilder builder;
builder.AddListeningPort("0.0.0.0:50051", grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr server = builder.BuildAndStart(); // 注意是 BuildAndStart
std::cout << "Server listening on 0.0.0.0:50051\n";
server->Wait();
客户端同步调用易阻塞主线程,高并发场景务必用异步 API + CompletionQueue
用 stub->SayHello(&context, request, &response) 这种同步方式,在 QPS > 100 时容易因网络延迟堆积线程,最终耗尽内存或触发 OS 线程创建失败。
立即学习“C++免费学习笔记(深入)”;
- 异步模式下,每个 RPC 调用只占少量栈空间,靠
CompletionQueue::Next()统一收包 - 不要为每个请求 new 一个
CompletionQueue;推荐复用 1–4 个全局队列,按负载绑定线程 - 注意
ClientContext生命周期:必须在AsyncNext()返回前保持有效,否则回调中访问 context 成员会 crash - 超时必须设在
ClientContext::set_deadline(),而不是靠外部 timer —— gRPC 内部 deadline 检查更精确








