
FlatBuffers 与 gRPC 的 TypeScript 实践Greeter 示例服务端与客户端完整运行指南【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers本指南以仓库中 grpc/examples/ts/greeter/README.md 为核心讲解如何在 TypeScript 中构建一个基于 FlatBuffers 二进制序列化格式的 gRPC Greeter 服务从环境准备、依赖安装、Schema 定义到服务端一元调用 服务端流式调用与客户端的完整实现最后给出可复制的构建与运行命令。读完本文你将掌握「FlatBuffers 数据 gRPC 传输」在 Node.js/TypeScript 下的最小可运行闭环并理解生成代码与手写业务代码的协作方式。一、示例概览用 FlatBuffers 作为 gRPC 的载荷格式gRPC 默认使用 protobuf 作为序列化协议但 gRPC 本身允许替换消息序列化实现。本示例正是演示了这一组合用 FlatBuffers 编译器flatc生成的 TypeScript 代码作为消息类型配合官方grpc/grpc-js运行时完成 RPC 通信。相比 protobufFlatBuffers 的最大特点是零拷贝反序列化——访问字段时无需先解析整个缓冲区直接通过偏移量读取内存中的二进制数据这使其非常适合对延迟和内存敏感的服务间通信场景。示例中定义了两个 RPCSayHello一元调用Unary客户端发送一个名字服务端返回问候语SayManyHellos服务端流式调用Server Streaming服务端用不同语言连续返回多条问候语。完整示例代码位于 grpc/examples/ts/greeter仓库根目录下还有同构的 Go、Python、Swift 版本可对照学习见 grpc/examples。二、环境准备与依赖按原文档要求运行前需要确认机器上已安装TypeScript以及 npm 环境用于拉取依赖。整个示例仅依赖两个 npm 包声明在 grpc/examples/ts/greeter/package.json 中{ name: flatbuffers-js-grpc, version: 1.0.0, scripts: { build: npm install tsc, client: node dist/client.js, server: node dist/server.js }, dependencies: { grpc/grpc-js: ^1.3.2, flatbuffers: ^2.0.0 } }grpc/grpc-jsgRPC 的纯 JavaScript 官方实现负责 HTTP/2 传输、服务注册与 RPC 调度flatbuffersFlatBuffers 的 TypeScript 运行时库提供Builder序列化与ByteBuffer反序列化核心类源码见 ts/builder.ts 与 ts/byte-buffer.ts。package.json中的三个 npm scripts 与 README 中的命令一一对应build负责安装依赖并编译server/client分别启动服务端与客户端。TypeScript 编译配置grpc/examples/ts/greeter/tsconfig.json 采用 CommonJS 模块体系编译产物输出到dist/目录{ compilerOptions: { target: es5, module: commonjs, outDir: ./dist, allowJs: true, sourceMap: true, strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true } }其中outDir: ./dist是关键——npm scripts 中node dist/server.js与node dist/client.js读取的正是编译产物。因此构建必须先于运行完成。三、Schema 定义FlatBuffers 侧的「协议文件」与 protobuf 使用.proto文件类似FlatBuffers 使用.fbs文件描述消息结构。本示例的 Schema 是 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); }逐段解读namespace models;生成代码的命名空间/模块名对应src/models.ts及其子模块两张tableHelloRequest含一个字符串字段nameHelloReply含一个字符串字段message。FlatBuffers 的 table 是可演进的结构字段按 id 顺序存储新增字段不会破坏旧数据rpc_service Greeter声明 gRPC 服务。SayHello是标准一元调用SayManyHellos通过(streaming: server)标记为服务端流式调用即客户端发一次请求服务端可多次返回HelloReply。用 flatc 编译器配合 gRPC 插件见 grpc/src/compiler对上述.fbs编译后会生成两大部分代码消息代码src/models/hello-request.ts与src/models/hello-reply.ts——每个 table 对应一个类提供getRootAsXxx、createXxx、startXxx、addXxx、endXxx等静态方法gRPC 绑定代码src/greeter_grpc.d.ts类型声明与src/greeter_grpc.js——包含GreeterService服务定义、IGreeterServer服务端接口、GreeterClient客户端类。这些生成文件均以// automatically generated by the FlatBuffers compiler, do not modify开头业务代码只负责 import 使用不要手工修改。四、服务端实现一元调用与服务端流式调用服务端代码位于 grpc/examples/ts/greeter/src/server.ts它实现了IGreeterServer接口中的两个方法再注册到 gRPC Server 上。4.1 实现 SayHello一元调用const greeter: IGreeterServer { SayHello( call: grpc.ServerUnaryCallHelloRequest, HelloReply, callback: grpc.sendUnaryDataHelloReply, ): void { console.log(SayHello ${call.request.name()}); const builder new flatbuffers.Builder(); const offset builder.createString(welcome ${call.request.name()}); const root HelloReply.createHelloReply(builder, offset); builder.finish(root); callback( null, HelloReply.getRootAsHelloReply( new flatbuffers.ByteBuffer(builder.asUint8Array()), ), ); }, ...关键点读取请求call.request.name()直接从HelloRequest对象上读取字段底层是 hello-request.ts 中通过bb.__offset/bb.__string完成的零拷贝访问没有中间解析副本构造响应先new flatbuffers.Builder()创建构建器用createString写入字符串再用HelloReply.createHelloReply(builder, offset)一次完成「start → add → end」三步可对照 hello-reply.ts 中createHelloReply的实现最后builder.finish(root)收尾返回数据builder.asUint8Array()取出序列化字节包一层ByteBuffer后通过callback(null, reply)返回。null表示无错误。4.2 实现 SayManyHellos服务端流式调用async SayManyHellos( call: grpc.ServerWritableStreamHelloRequest, HelloReply, ): Promisevoid { const name call.request.name(); console.log(${call.request.name()} saying hi in different langagues); [Hi, Hallo, Ciao].forEach((element) { const builder new flatbuffers.Builder(); const offset builder.createString(${element} ${name}); const root HelloReply.createHelloReply(builder, offset); builder.finish(root); call.write( HelloReply.getRootAsHelloReply( new flatbuffers.ByteBuffer(builder.asUint8Array()), ), ); }); call.end(); }与一元调用的差异在于不再使用callback而是通过流对象call.write(...)逐条发送三条问候语Hi/Hallo/Ciao最后调用call.end()结束流。每条消息都独立走一遍 FlatBuffers 序列化流程这也体现了流式场景下「每次 write 一个独立二进制消息」的载荷组织方式。4.3 启动服务function serve(): void { const PORT 3000; const server new grpc.Server(); server.addService(GreeterService, greeter); console.log(Listening on ${PORT}); server.bindAsync( localhost:${PORT}, grpc.ServerCredentials.createInsecure(), (err: Error | null, port: number) { if (err) { console.error(Server error: ${err.message}); } else { console.log(Server bound on port: ${port}); server.start(); } }, ); } serve();GreeterService来自生成的 greeter_grpc.d.ts其中为每个 RPC 声明了path如/models.Greeter/SayHello、流模式以及序列化/反序列化函数addService据此完成路由注册监听地址固定为localhost:3000使用createInsecure()明文凭据仅用于本地演示生产环境应替换为 TLS 凭据bindAsync是 gRPC-js 推荐的异步绑定方式回调中拿到实际绑定端口后再server.start()。五、客户端实现一元调用与流式响应订阅客户端代码位于 grpc/examples/ts/greeter/src/client.ts。5.1 构造连接与请求消息const client new GreeterClient( localhost:${PORT}, grpc.credentials.createInsecure(), ); const builder new flatbuffers.Builder(); const offset builder.createString(name); const root HelloRequest.createHelloRequest(builder, offset); builder.finish(root); const buffer HelloRequest.getRootAsHelloRequest( new flatbuffers.ByteBuffer(builder.asUint8Array()), );GreeterClient继承自grpc.Client见 greeter_grpc.d.ts 的GreeterClient类声明构造时传入服务端地址与凭据。请求消息的构造与服务端完全对称Builder→createHelloRequest→finish→getRootAsHelloRequest最终拿到可直接发送的HelloRequest对象。5.2 调用 SayHello一元client.SayHello(buffer, (err, response) { console.log(response.message()); });回调中直接打印response.message()——与服务端相同读取字段不经过解析这正是 FlatBuffers 零拷贝优势在客户端侧的直接体现。5.3 调用 SayManyHellos流式const data client.SayManyHellos(buffer, null); data.on(data, (data) { console.log(data.message()); });服务端流式调用返回grpc.ClientReadableStream客户端通过 Node 事件机制订阅data事件逐条消费响应每条数据都是一个反序列化好的HelloReply。5.4 命令行参数解析const args process.argv.slice(2); const PORT Number(args[0]); const name: string args[1] ?? flatbuffers; if (PORT) { main(PORT, name); } else { throw new Error(Requires a valid port number.); }客户端要求第一个参数为端口号与npm run client 3000对应第二个参数为可选的问候名字缺省时使用flatbuffers未传端口则直接抛错保证调用方必须显式指定目标端口。六、构建与运行完整命令序列依据 grpc/examples/ts/greeter/README.md 与 package.json 中的 scripts完整运行流程如下。第一步构建在示例目录仓库中为grpc/examples/ts/greeter/下执行二选一npm run build等价于npm install tscnpm install会安装grpc/grpc-js与flatbufferstsc按tsconfig.json将src/下的 TypeScript 编译到dist/。构建成功后dist/中应存在server.js与client.js。第二步启动服务端npm run server即node dist/server.js。启动后控制台输出Listening on 3000随后在bindAsync回调中输出Server bound on port: 3000服务开始监听localhost:3000。此时可看到SayHello flatbuffers、flatbuffers saying hi in different langagues等服务端日志随请求打印。第三步启动客户端npm run client 3000即node dist/client.js 3000。客户端以3000为端口连接到本地服务端姓名参数缺省为flatbuffers。预期输出welcome flatbuffers # SayHello 的一元响应 Hi flatbuffers # SayManyHellos 流式响应第 1 条 Hallo flatbuffers # 第 2 条 Ciao flatbuffers # 第 3 条也可显式传入姓名例如npm run client 3000 Alice响应中的问候对象会相应变为welcome Alice、Hi Alice等。运行注意事项必须先完成构建dist/存在再运行 server / client否则node dist/...会因文件不存在而失败服务端与客户端需在不同终端中分别执行先启动服务端再启动客户端示例使用localhost与不安全凭据仅限本机演示跨主机或生产部署需改用可路由地址与 TLS 凭据。七、理解生成代码FlatBuffers 与 gRPC 的粘合层7.1 消息类的序列化协议以 hello-reply.ts 为例每个生成的 table 类都遵循同一模式getRootAsHelloReply(bb)从ByteBuffer当前偏移处解析根对象message()字段访问器内部通过this.bb.__offset(...)判断字段是否存在兼容缺失字段再__string(...)取字符串找不到时返回nullstartHelloReply/addMessage/endHelloReply分段构建 APIcreateHelloReply则是三者的便捷封装serialize()/deserialize()在Uint8Array与对象之间互转便于配合 gRPC 的序列化钩子。这种「构建期分段写入、读取期按偏移直接访问」的设计正是 FlatBuffers 无解析开销的底层来源。7.2 gRPC 绑定代码的分工greeter_grpc.d.ts 展示了生成绑定的完整契约GreeterService实现grpc.ServiceDefinition为SayHellorequestStreamfalse, responseStreamfalse与SayManyHellosrequestStreamfalse, responseStreamtrue声明path、流模式与序列化函数——服务端据此注册客户端据此寻址方法路径为/models.Greeter/SayHello与/models.Greeter/SayManyHellosIGreeterServer服务端实现接口SayHello用grpc.handleUnaryCallSayManyHellos用grpc.handleServerStreamingCall签名约束GreeterClient客户端类SayHello提供带回调的多重重载SayManyHellos返回ClientReadableStreamHelloReply。7.3 同构示例与进一步探索若想对比其他语言下同一 Schema 的实现可查看Go 版本grpc/examples/go含 client/server 与独立的 hero、net 模块Python 版本grpc/examples/pythonSwift 版本grpc/examples/swift。三个版本共享同一份 greeter.fbs体现了 FlatBuffers「一份 Schema多语言生成」的特性。仓库中的 gRPC 测试如 grpc/tests/grpctest.cpp、grpc/tests/go_test.go与 gRPC 代码生成器源码 grpc/src/compiler 可帮助你进一步理解生成逻辑与运行时行为。八、小结本文围绕 grpc/examples/ts/greeter/README.md 给出的运行步骤完整拆解了 TypeScript 下 FlatBuffers gRPC 的示例闭环Schema 层greeter.fbs定义消息与rpc_servicestreaming: server声明流式模式生成代码层消息类HelloRequest/HelloReply负责零拷贝读写greeter_grpc绑定负责 gRPC 路由与调用业务代码层服务端实现IGreeterServer两个方法并注册监听客户端构造请求并分别以回调与事件流方式消费响应运行流程npm run build或npm install tsc→npm run server→npm run client 3000。掌握这套组合后你可以将此模式推广到任意「需要低开销序列化 gRPC 服务治理」的 Node.js/TypeScript 场景并通过修改.fbs文件重新生成代码来快速演进协议。【免费下载链接】flatbuffersFlatBuffers: Memory Efficient Serialization Library项目地址: https://gitcode.com/GitHub_Trending/fl/flatbuffers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考