FlatBuffers 与 gRPC 集成指南:构建、测试与 C++ Callback API 代码生成
【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers
本篇技术指南以 grpc/README.md 为核心,系统讲解在 FlatBuffers 仓库中如何构建并运行 gRPC 集成测试,以及如何通过flatc生成基于现代 gRPC Callback API 的 C++ 服务端与客户端代码。读完本文,你将掌握 Linux 环境下 gRPC + FlatBuffers 的完整构建流程、Bazel 替代方案、测试运行方式,以及四种 RPC 流式形态(Unary / Client streaming / Server streaming / Bidi streaming)对应的 reactor 返回类型与async_*客户端方法签名。
gRPC 支持在 FlatBuffers 仓库中的组织方式
FlatBuffers 仓库中的grpc/目录承载了与 gRPC 相关的全部内容,整体布局如下:
grpc/src/compiler/:与 gRPC 项目共享的代码生成器源码,按语言拆分为 cpp_generator.cc、go_generator.cc、java_generator.cc、python_generator.cc、swift_generator.cc、ts_generator.cc 等,配合 schema_interface.h 提供统一的 schema 访问抽象;grpc/tests/:gRPC 专属测试(如 grpctest.cpp 以及回调 API 的编译期测试);grpc/examples/:Go、Python、Swift、TypeScript 四种语言的 greeter 示例,schema 见 greeter.fbs;grpc/samples/greeter/:C++ 版 greeter 示例(client.cpp / server.cpp);grpc/flatbuffers-java-grpc/:Java 侧 gRPC 工具类。
文档强调了一个重要约定:grpc/src/下的文件与 gRPC 项目共享,并在 gRPC 仓库中维护,任何改动都应提交到 gRPC 项目而非 FlatBuffers 仓库。这些文件是从 gRPC 拷贝而来,同时服务于 Protobuf 与 FlatBuffers 两套代码生成器——这从 cpp_generator.h 的注释可以得到印证:“cpp_generator.h/.cc 不直接依赖 gRPC/ProtoBuf,因此可以被用于为其他序列化系统(如 FlatBuffers)生成代码”。
grpc/tests/中存放的是 gRPC 专属测试。要编译这些测试,必须先构建并安装 gRPC 库,然后通过主 FlatBuffers CMake 工程的FLATBUFFERS_BUILD_GRPCTEST选项开启编译。
在 Linux 上构建带 gRPC 的 FlatBuffers
前置准备:下载并安装 gRPC
- 下载、构建并安装 gRPC(可参考 gRPC 官方 C++ 安装指引,例如克隆 gRPC 仓库)。
- 假设你的 gRPC 克隆位于
/your/path/to/grpc_repo。 - 使用自定义安装目录安装 gRPC:
make install prefix=/your/path/to/grpc_repo/install
配置环境变量
构建 gRPC 测试需要两个环境变量,它们分别告诉 CMake gRPC 的安装位置与 Protobuf 源码位置:
export GRPC_INSTALL_PATH=/your/path/to/grpc_repo/install export PROTOBUF_DOWNLOAD_PATH=/your/path/to/grpc_repo/third_party/protobuf配置并编译
mkdir build ; cd build cmake -DFLATBUFFERS_BUILD_GRPCTEST=ON -DGRPC_INSTALL_PATH=${GRPC_INSTALL_PATH} -DPROTOBUF_DOWNLOAD_PATH=${PROTOBUF_DOWNLOAD_PATH} .. make这三个 CMake 变量在主 CMakeLists.txt 中有严格校验:FLATBUFFERS_BUILD_GRPCTEST默认为 OFF(第 27 行定义),当开启后,若未定义GRPC_INSTALL_PATH或PROTOBUF_DOWNLOAD_PATH,CMake 会直接报错并提示“See grpc/README.md”。校验通过后,CMake 会将${GRPC_INSTALL_PATH}/include与${PROTOBUF_DOWNLOAD_PATH}/src加入 include 搜索路径,并把${GRPC_INSTALL_PATH}追加进CMAKE_PREFIX_PATH以帮助查找 gRPC 库。
Bazel 用户
如果你使用 Bazel 构建,可直接运行:
bazel test src/compiler/...该命令会验证grpc/src/compiler/下的各语言生成器(BUILD 定义见 grpc/src/compiler/BUILD.bazel)。
运行 FlatBuffers gRPC 测试
Linux
先让动态链接器找到 gRPC 的共享库,再运行测试:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${GRPC_INSTALL_PATH}/lib make test ARGS=-VARGS=-V会以 verbose 模式输出每个测试用例的详细结果。
以 grpctest.cpp 为例,该测试覆盖了典型的 gRPC 集成场景:
- 服务端派生自生成的
MonsterStorage::Service,实现 Unary RPCStore(用fbb_.ReleaseMessage<Stat>()把MessageBuilder构建的响应所有权移交给 gRPC)与 Server streaming RPCRetrieve(通过writer->Write(monster)循环发送 5 条消息); - 客户端通过
grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials())连接,并分别用MessageBuilder与普通FlatBufferBuilder各执行一次StoreRPC与RetrieveRPC; - 当
FLATBUFFERS_GRPC_DISABLE_AUTO_VERIFICATION未定义时,还会构造一个无效请求,断言 RPC 返回StatusCode::INTERNAL且错误消息为 “Message verification failed”,验证 gRPC 消息自动校验机制。
Bazel 用户
bazel test tests/...C++ Callback API 代码生成
生成命令
FlatBuffers 的 gRPC C++ 代码生成目前可选支持现代 gRPC Callback API。在现有Service与异步 mixin 之外,同时生成CallbackService骨架的方式是让flatc同时携带--grpc与--grpc-callback-api两个选项:
flatc --cpp --grpc --grpc-callback-api your_service.fbs从源码看,flatc解析到该选项后,会在 idl_gen_grpc.cpp 中将其写入generator_parameters.generate_callback_api,最终传递给 gRPC 侧的生成器——对应 cpp_generator.h 中Parameters结构体的bool generate_callback_api = false字段(默认关闭,显式开启才生成)。
生成的 CallbackService
开启该选项后,生成的头文件会在宏保护下新增一个类(原文档示例):
class YourService::CallbackService : public ::grpc::Service { /* reactor virtuals */ };该类的实际生成逻辑位于 cpp_generator.cc:先输出#if defined(GRPC_CALLBACK_API_NONEXPERIMENTAL)保护宏,再打印CallbackService的前向声明与类定义,并声明每个 RPC 对应的 reactor 虚函数;末尾以#endif // GRPC_CALLBACK_API_NONEXPERIMENTAL收尾,保证旧版 gRPC 库下不产生破坏性变更。
每种 RPC 形态映射到相应的 reactor 返回类型:
| RPC 形态 | CallbackService 方法返回类型 |
|---|---|
| Unary | ::grpc::ServerUnaryReactor* |
| Client streaming | ::grpc::ServerReadReactor<Request>* |
| Server streaming | ::grpc::ServerWriteReactor<Response>* |
| Bidi streaming | ::grpc::ServerBidiReactor<Request, Response>* |
默认生成的方法实现返回nullptr,需要在你的派生类中覆写并返回一个由你管理的 reactor 实例(生命周期模式参考 gRPC 官方文档)。若你的 gRPC 库早于稳定版回调 API 宏(即不存在GRPC_CALLBACK_API_NONEXPERIMENTAL),保护宏内的代码会被整体跳过,不影响原有编译。使用该特性请确保构建基于较新的 gRPC(1.38+,具体最低版本以 gRPC 仓库为准)。
仓库中提供了对应的编译期验证测试:grpctest_callback_compile.cpp 在FLATBUFFERS_GENERATED_GRPC_CALLBACK_API与GRPC_CALLBACK_API_NONEXPERIMENTAL同时定义时,派生一个CallbackServiceImpl : public MyGame::Example::MonsterStorage::CallbackService并实例化,用于确认生成的服务端骨架可编译(仅编译验证,不执行任何 RPC)。
客户端 Callback Stubs
当提供--grpc-callback-api时,生成的 C++ 客户端 stub 会在原有的同步 / 通用异步风格之外,新增基于原生回调 / reactor 的异步方法,并由同一个宏保护。对每个名为Foo的 RPC,生成的方法签名如下(摘自原文档):
Unary:
void async_Foo(::grpc::ClientContext*, const Request&, Response*, std::function<void(::grpc::Status)>); void async_Foo(::grpc::ClientContext*, const Request&, Response*, ::grpc::ClientUnaryReactor*);Client streaming:
::grpc::ClientWriteReactor<Request>* async_Foo(::grpc::ClientContext*, Response*, ::grpc::ClientWriteReactor<Request>*);Server streaming:
::grpc::ClientReadReactor<Response>* async_Foo(::grpc::ClientContext*, const Request&, ::grpc::ClientReadReactor<Response>*);Bidirectional streaming:
::grpc::ClientBidiReactor<Request, Response>* async_Foo(::grpc::ClientContext*, ::grpc::ClientBidiReactor<Request, Response>*);这些方法直接映射到原生 gRPC 回调 API 工厂(如CallbackUnaryCall、ClientCallbackWriterFactory::Create等),不会自行创建线程,I/O 驱动需要按 gRPC 文档覆写相应的 reactor 回调。若构建使用的 gRPC 缺少非实验性宏,这些符号将不会被生成,从而保持向后兼容。
客户端侧的编译期验证见 grpctest_callback_client_compile.cpp:它通过static_assert检查Stub::async_Store(Unary 的函数形式与 reactor 形式)、Stub::async_Retrieve(Server streaming 的ClientReadReactor)、Stub::async_GetMaxHitPoint(Client streaming 的ClientWriteReactor)以及Stub::async_GetMinMaxHitPoints(Bidi 的ClientBidiReactor)等成员函数指针是否存在,属于纯编译期测试,不发起实际 RPC。
多语言示例与常见注意事项
grpc/examples/下的 greeter 示例展示了同一种 schema 在多种语言中的落地方式:
- grpc/examples/go/greeter:Go 客户端与服务端;
- grpc/examples/python/greeter:Python 客户端与服务端;
- grpc/examples/swift/Greeter:Swift 实现;
- grpc/examples/ts/greeter:TypeScript 实现;
- grpc/samples/greeter:C++ 版(含 Makefile)。
示例 schema 定义了一个典型的rpc_service(见 grpc/examples/greeter.fbs):
namespace models; table HelloReply { message:string; } table HelloRequest { name:string; } rpc_service Greeter { SayHello(HelloRequest):HelloReply; SayManyHellos(HelloRequest):HelloReply (streaming: "server"); }grpc/examples/README.md 记录了两种语言的已知问题,值得在实际开发中留意:
- Python:由于 Python 端可能收到
Bytes array或utf8 string,服务端 / 客户端需要自行断言类型。例如在SayHello中应先通过HelloRequest.HelloRequest().GetRootAs(request, 0)解析,再对字符串做reply.decode('UTF-8')编码处理;更稳妥的做法是保证所有进出 Python 的请求都是Bytes array,如hello_request = bytes(builder.Output()); - Go:payload 的
content-type必须设置为application/grpc+flatbuffers,例如.SayHello(ctx, b, grpc.CallContentSubtype("flatbuffers"))。
小结
围绕 grpc/README.md,本文完整覆盖了 FlatBuffers 与 gRPC 集成的三个层面:构建(CMake 的FLATBUFFERS_BUILD_GRPCTEST与两个路径变量,或 Bazel 的bazel test)、测试运行(make test ARGS=-V与LD_LIBRARY_PATH),以及--grpc-callback-api带来的现代 C++ 回调式代码生成。其中 Callback API 部分既有生成代码的形态说明(四种 RPC 映射的 reactor 类型、客户端四种async_*签名),也有仓库中两份编译期测试作为落地验证。配合多语言 greeter 示例与已知问题清单,你可以据此快速搭建基于 FlatBuffers 序列化的 gRPC 服务。
【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考