news 2026/9/11 22:06:14

FlatBuffers Go gRPC Greeter 示例:从 .fbs 定义到可运行的服务端与客户端

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
FlatBuffers Go gRPC Greeter 示例:从 .fbs 定义到可运行的服务端与客户端

FlatBuffers Go gRPC Greeter 示例:从 .fbs 定义到可运行的服务端与客户端

【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers

本篇技术指南以仓库中 grpc/examples/go/greeter/README.md 为核心,完整讲解如何基于 FlatBuffers 构建一个 Go 语言的 gRPC 服务:从编写greeter.fbs的 RPC 服务定义,到生成 Go 模型代码与 gRPC 桩代码,再到启动服务端、调用客户端的完整流程。读完本文,你将掌握 FlatBuffers 作为 gRPC 序列化格式的接入方式、FlatbuffersCodec的编解码原理、一元 RPC(SayHello)与服务端流式 RPC(SayManyHellos)的完整实现细节,并可直接在本地复现运行。

示例整体结构与模块划分

示例采用三模块布局,与 README 中给出的目录结构一一对应:

grpc/examples/go/greeter/ ├── server # 服务端模块:监听端口、注册 Greeter 服务 ├── client # 客户端模块:构建 FlatBuffers 请求并调用 ├── models # FlatBuffers 模型代码与 gRPC 主代码(由 flatc 生成) └── README.md
  • server/main.go:gRPC 服务端入口,注册 Greeter 服务并监听localhost:3000
  • client/main.go:gRPC 客户端入口,通过--name参数指定要发送的名字,依次调用SayHelloSayManyHellos
  • models/:由 FlatBuffers 编译器(flatc)生成的HelloRequest.goHelloReply.go以及 gRPC Go 插件生成的Greeter_grpc.go,是服务端与客户端共享的公共代码。

三个模块各自拥有独立的go.modclientserver都通过replace指令把models模块指向本地../models目录,例如 server/go.mod 中的:

replace github.com/google/flatbuffers/grpc/examples/go/greeter/models v0.0.0 => ../models

这种方式避免了把生成的模型代码发布到远程仓库,本地开发时直接复用同一份models包,保证两端消息结构一致。

服务定义:greeter.fbs 中的 RPC 描述

整个示例的业务协议定义在 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"); }

从中可以看到 FlatBuffers 描述 gRPC 服务的三个关键语法要素:

  1. table定义消息体HelloRequest携带一个name字符串字段,HelloReply携带一个message字符串字段,与常规 FlatBuffers schema 完全一致;
  2. rpc_service关键字:声明 gRPC 服务,本例服务名为Greeter
  3. (streaming: "server")标注SayHello是标准的一元 RPC(一次请求、一次响应);SayManyHellos声明为服务端流式 RPC,客户端发送一次请求,服务端可以连续推送多条HelloReply

在 grpc/examples/greeter_v2.fbs 中还有该服务的第二版本定义(用于演示 Swift 等其他语言实现),Go 示例使用的是上述greeter.fbs

生成模型代码:models 模块的产物

models目录下的三个文件是「生成代码 + 手写代码」协作的结果:

  • HelloRequest.go/HelloReply.go:由flatc --go生成,文件头标注 "Code generated by the FlatBuffers compiler. DO NOT EDIT.";
  • Greeter_grpc.go:由 gRPC Go 插件生成,文件头标注 "Generated by gRPC Go plugin",其中RegisterGreeterServerNewGreeterClient等正是服务端与客户端 main.go 直接调用的入口。

HelloRequest 的读写接口

以 models/HelloRequest.go 为例,它展示了 FlatBuffers 生成的典型 API 形态:

  • GetRootAsHelloRequest(buf, offset):从字节缓冲区反序列化出请求对象;
  • HelloRequestStart(builder)/HelloRequestAddName(builder, name)/HelloRequestEnd(builder):使用flatbuffers.Builder从零构建一条请求;
  • Name():以[]byte形式读取name字段,未设置时返回nil

Greeter 服务的 gRPC 桩

models/Greeter_grpc.go 中与标准 gRPC 生成代码最大的不同在于消息类型:接口方法签名直接使用*flatbuffers.Builder作为请求/响应载体,而不是 protobuf 消息对象。例如:

type GreeterClient interface { SayHello(ctx context.Context, in *flatbuffers.Builder, opts ...grpc.CallOption) (*HelloReply, error) SayManyHellos(ctx context.Context, in *flatbuffers.Builder, opts ...grpc.CallOption) (Greeter_SayManyHellosClient, error) }

服务端侧接口GreeterServer则反向使用*HelloRequest作为入参、*flatbuffers.Builder作为返回值:

type GreeterServer interface { SayHello(context.Context, *HelloRequest) (*flatbuffers.Builder, error) SayManyHellos(*HelloRequest, Greeter_SayManyHellosServer) error mustEmbedUnimplementedGreeterServer() }

服务描述_Greeter_serviceDesc中注册了名为models.Greeter的服务:SayHello走一元MethodDescSayManyHellos走服务端流式StreamDescServerStreams: true),这正是greeter.fbs中两种 RPC 形态在生成代码层面的落点。

运行服务端

按照 README 的步骤,在grpc/examples/go/greeter/server目录下执行:

cd server go clean go run main.go

服务端启动后监听localhost:3000,其启动逻辑集中在 server/main.go 的main()中,核心只有三步:

  1. 监听 TCP 端口net.Listen("tcp", "localhost:3000")
  2. 注入 FlatBuffers 编解码器
codec := &flatbuffers.FlatbuffersCodec{} grpcServer := grpc.NewServer(grpc.ForceServerCodec(codec))

这一行是整个示例的关键:gRPC 默认使用 protobuf 编解码,而grpc.ForceServerCodec强制服务端改用flatbuffers.FlatbuffersCodec,从而让 gRPC 帧内直接承载 FlatBuffers 二进制,实现"零拷贝、无中间对象"的高效传输; 3.注册服务并启动models.RegisterGreeterServer(grpcServer, newServer())后调用grpcServer.Serve(lis)

SayHello:一元 RPC 的服务端实现

func (s *greeterServer) SayHello(ctx context.Context, request *models.HelloRequest) (*flatbuffers.Builder, error) { v := request.Name() var m string if v == nil { m = "Unknown" } else { m = string(v) } b := flatbuffers.NewBuilder(0) idx := b.CreateString("welcome " + m) models.HelloReplyStart(b) models.HelloReplyAddMessage(b, idx) b.Finish(models.HelloReplyEnd(b)) return b, nil }

实现细节值得注意:

  • 通过request.Name()读取请求中的名字,若字段为空则回退为"Unknown",体现 FlatBuffers 可选字段nil判断的惯用法;
  • 响应不构造中间对象,直接用flatbuffers.NewBuilder(0)逐字段写入HelloReplyFinish,最终返回的是*flatbuffers.Builder而非序列化后的字节切片,由 gRPC 框架配合FlatbuffersCodec完成后续写帧。

SayManyHellos:服务端流式 RPC 的实现

for _, greeting := range greetings { // greetings = [...]string{"Hi", "Hallo", "Ciao"} idx := b.CreateString(greeting + " " + m) models.HelloReplyStart(b) models.HelloReplyAddMessage(b, idx) b.Finish(models.HelloReplyEnd(b)) if err := stream.Send(b); err != nil { return err } }

服务端在单个请求内循环三次,通过stream.Send(b)连续推送三条问候消息(HiHalloCiao),体现服务端流式 RPC 的语义:一次请求、多次响应,直到函数返回。

运行客户端

按照 README 的步骤,在grpc/examples/go/greeter/client目录下执行:

cd client go clean go run main.go --name NAME

其中--name是客户端定义的命令行参数,默认值为"Flatbuffers",来自 client/main.go 中的:

name = flag.String("name", "Flatbuffers", "name to be sent to server :D")

客户端连接与调用逻辑同样分为几步:

建立连接并指定编解码器

conn, err := grpc.Dial(fmt.Sprintf("localhost:%d", 3000), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultCallOptions(grpc.ForceCodec(flatbuffers.FlatbuffersCodec{})))
  • 服务端未配置 TLS,因此客户端必须使用insecure.NewCredentials(),否则握手会失败;
  • grpc.WithDefaultCallOptions(grpc.ForceCodec(...))grpc.CallContentSubtype("flatbuffers")共同作用,确保所有调用的请求/响应都按 FlatBuffers 编解码。

构建请求并调用 SayHello

b := flatbuffers.NewBuilder(0) i := b.CreateString(name) models.HelloRequestStart(b) models.HelloRequestAddName(b, i) b.Finish(models.HelloRequestEnd(b)) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() request, err := client.SayHello(ctx, b, grpc.CallContentSubtype("flatbuffers")) log.Printf("server said %q", request.Message())

请求的构建与服务端一致:先CreateString创建字符串偏移,再HelloRequestStartHelloRequestAddNameHelloRequestEndFinish。调用时设置 10 秒超时上下文,并把*flatbuffers.Builder直接作为请求参数传入。响应*HelloReply通过request.Message()读取服务端回写的问候语。

消费服务端流 SayManyHellos

stream, err := client.SayManyHellos(ctx, b, grpc.CallContentSubtype("flatbuffers")) for { request, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf(...) } log.Printf("server said %q", request.Message()) }

客户端循环调用stream.Recv()逐条接收服务端推送的消息,直到收到io.EOF才结束——这是 gRPC Go 服务端流式客户端收包的标准写法。按默认参数运行,客户端将依次收到三条问候:

server said "welcome Flatbuffers" server said "Hi Flatbuffers" server said "Hallo Flatbuffers" server said "Ciao Flatbuffers"

依赖与 Go 版本要求

三个模块的go.mod均声明go 1.15,核心依赖为:

  • github.com/google/flatbuffers v2.0.8+incompatible:FlatBuffers Go 运行时库,提供flatbuffers.BuilderFlatbuffersCodec等;
  • google.golang.org/grpc v1.56.3:gRPC Go 框架。

clientserver模块额外通过replace指令引用本地models模块。由于示例本身位于 FlatBuffers 仓库内,直接go run时 Go 模块解析会命中仓库自身的依赖;若将其复制到仓库外使用,需要把github.com/google/flatbuffers替换为你实际引入的 FlatBuffers 版本路径。

从零复现:自己动手生成模型代码

仓库中已提交生成好的models代码,但如果你想从greeter.fbs重新生成(例如修改了 schema),流程是:

  1. 先构建flatc(FlatBuffers 编译器),参见 docs/building.md 中的构建说明;
  2. 在 grpc/examples 目录下执行flatc --go --grpc -o go/greeter/models greeter.fbs
  3. 生成的HelloRequest.goHelloReply.goGreeter_grpc.go会写入models目录,其中 gRPC 桩需要--grpc选项并链接 gRPC Go 插件。

仓库还提供了批量重新生成的辅助脚本,可参考 scripts/generate_grpc_examples.py 与 grpc/examples/go/format.sh,后者用于对生成代码执行gofmt格式化。

与仓库其他语言示例的对照

同一份greeter.fbs在仓库中被翻译成了多种语言的完整示例,可作为学习参照:

  • Python:见 grpc/examples/python/greeter/README.md,包含server.py/client.py
  • Swift:见 grpc/examples/swift/Greeter/README.md;
  • TypeScript:见 grpc/examples/ts/greeter/README.md。

gRPC 的框架级测试则集中在 grpc/tests 目录,其中 grpc/tests/grpctest.cpp 与 grpc/tests/grpctest.py 对FlatbuffersCodec在 C++ 与 Python 侧的接入做了系统性验证,可作为深入理解编解码器实现与边界行为的补充材料。

小结

这个 Go Greeter 示例浓缩了 FlatBuffers 与 gRPC 集成的全部关键点:用rpc_service.fbs中声明服务、用flatc --grpc生成 Go 桩代码、在服务端与客户端两侧通过FlatbuffersCodec替换默认 protobuf 编解码,并分别演示了一元 RPC 与服务端流式 RPC 的完整读写流程。理解它之后,把现有 protobuf gRPC 服务迁移到 FlatBuffers 序列化,或在新的 Go 服务中采用零拷贝传输方案,都可以直接照搬这套结构。

【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 22:01:39

WorkBuddy智能体实战:把业主群电梯报修变成实时数据看板

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 21:59:02

10kW级VSG预同步并网控制Matlab实现详解

1. 项目背景与核心价值虚拟同步发电机(VSG)技术是当前新能源并网领域的前沿研究方向,它通过模拟传统同步发电机的运行特性,使逆变器具备惯性和阻尼特性。这次我们要探讨的是10kW级VSG系统的预同步并网控制策略在Matlab中的实现方法…

作者头像 李华
网站建设 2026/9/11 21:57:36

基于Matlab的卫星轨道设计库:从开普勒根数到星下点轨迹

简介:基于Matlab的卫星轨道设计库,面向航天工程师、科研人员及高校相关专业学生,用于快速完成轨道参数计算、摄动分析与轨道仿真,解决从开普勒六参数到位置速度转换、多摄动源影响评估和轨道优化等实际问题。压缩包共20个文件&…

作者头像 李华
网站建设 2026/9/11 21:54:17

PLMS自适应滤波器:抗脉冲噪声的Matlab实现与优化

1. PLMS自适应滤波器:噪声抑制的利器概率最小均方(PLMS)自适应滤波器是信号处理领域对抗噪声的一把瑞士军刀。不同于传统LMS滤波器对高斯噪声的偏爱,PLMS通过引入概率权重机制,在工业现场常见的非高斯噪声(…

作者头像 李华
网站建设 2026/9/11 21:54:00

WorkBuddy开放平台:个人开发者AI Agent应用构建指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华