grpc-go Route Guide 实战指南:四种 RPC 通信模式从零跑通
grpc-go Route Guide 实战指南四种 RPC 通信模式从零跑通【免费下载链接】grpc-goThe Go language implementation of gRPC. HTTP/2 based RPC项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-goRoute Guide 是 gRPC-Go 官方示例中覆盖面最完整的入门案例同一份服务定义里同时演示了 unary一元、server streaming服务端流式、client streaming客户端流式与 full duplex全双工/双向流式四种 RPC 模式并配齐了服务端、客户端与真实地理数据文件。本文基于 examples/route_guide/README.md 展开结合 route_guide.proto 服务定义、server.go 与 client.go 源码逐层拆解读完你将能独立编译、运行该示例理解四种流式 API 在 gRPC-Go 中的调用形态并掌握-tls、-port、-addr等全部命令行参数的实际用途。示例概览一个示例覆盖 gRPC 全部四种 RPC 模式gRPC 官方教程中的 Route Guide路线导航场景模拟了一个景点/地标查询服务服务端维护一张地理特征Feature列表客户端围绕这些点执行查询、遍历、轨迹上报与实时消息交换。gRPC-Go 仓库将这一教学场景完整落地为可直接运行的示例代码其核心目的正如 README 所描述The route guide server and client demonstrate how to use grpc go libraries to perform unary, client streaming, server streaming and full duplex RPCs.也就是说通过这一个示例即可掌握 gRPC-Go 的全部四种远程调用形态。README 中引用的完整教程为 gRPC 官方文档《gRPC Basics: Go》https://grpc.io/docs/tutorials/basic/go.html本仓库内的 route_guide.proto 即教程所对应的服务定义文件。示例的目录结构如下examples/route_guide/ ├── README.md # 本指南对应的说明文档 ├── client/ │ └── client.go # 客户端依次演示四种 RPC 调用 ├── server/ │ └── server.go # 服务端实现 RouteGuide 全部四个方法 ├── routeguide/ │ ├── route_guide.proto # 服务与消息定义edition 2023 │ ├── route_guide.pb.go # protoc-gen-go 生成的消息代码 │ └── route_guide_grpc.pb.go # protoc-gen-go-grpc 生成的服务代码 └── testdata/ └── route_guide_db.json # 100 条真实地理特征数据服务定义route_guide.proto 中的四种 RPC 形态服务定义位于 route_guide.proto它使用 Protobufedition 2023语法go_package指向google.golang.org/grpc/examples/route_guide/routeguide。RouteGuide服务共声明 4 个 RPC 方法恰好一一对应 gRPC 的四种通信模式service RouteGuide { // 一元 RPC请求一个点返回该点的特征 rpc GetFeature(Point) returns (Feature) {} // 服务端流式 RPC请求一个矩形区域流式返回区域内所有特征 rpc ListFeatures(Rectangle) returns (stream Feature) {} // 客户端流式 RPC连续上报路线上的点遍历结束后返回汇总 rpc RecordRoute(stream Point) returns (RouteSummary) {} // 双向流式 RPC一边发送路线笔记一边接收历史笔记 rpc RouteChat(stream RouteNote) returns (stream RouteNote) {} }对应地proto 中定义了 5 个消息类型每个都带有贴合业务语义的注释消息字段说明Pointint32 latitude、int32 longitudeE7 表示法的经纬度度数 × 10⁷ 后取整纬度范围 ±90°、经度范围 ±180°RectanglePoint lo、Point hi由两个对角点围成的经纬度矩形Featurestring name、Point location某点处的地标无地标时 name 为空字符串RouteNotePoint location、string message在某个点发送的笔记消息RouteSummarypoint_count、feature_count、distance、elapsed_timeRecordRoute 的汇总结果点数、命中的特征数、总里程米、总耗时秒值得注意的细节是ListFeatures的 proto 注释解释了为什么要用流式矩形区域可能覆盖很大范围、包含海量特征若像一元 RPC 那样一次性打包返回比如放在带 repeated 字段的响应消息里单条响应会过大因此采用服务端流式逐条下发。生成代码从 proto 到 Go 接口routeguide/目录下已经预生成好了两个 Go 文件无需手动执行 protocroute_guide.pb.go由protoc-gen-go生成的消息类型代码route_guide_grpc.pb.go由protoc-gen-go-grpc本仓库生成版本为 v1.6.2protoc v5.27.1生成的服务端/客户端代码。在 route_guide_grpc.pb.go 中可以看到四个方法的完整方法名常量例如RouteGuide_GetFeature_FullMethodName /routeguide.RouteGuide/GetFeature以及面向客户端与服务端的两套接口客户端接口RouteGuideClientGetFeature返回(*Feature, error)ListFeatures返回grpc.ServerStreamingClient[Feature]RecordRoute返回grpc.ClientStreamingClient[Point, RouteSummary]RouteChat返回grpc.BidiStreamingClient[RouteNote, RouteNote]——四种类型签名把四种流式语义固化在了类型系统里服务端接口RouteGuideServer要求实现全部四个方法服务端代码通过RegisterRouteGuideServer注册到grpc.Server。运行示例两条命令启动完整 demoREADME 给出的运行方式极为简单。假设当前位于examples/route_guide/目录下先启动服务端$ go run server/server.go再另开一个终端启动客户端$ go run client/client.go客户端默认会连到localhost:50051并依次执行完整演示序列查询已知地标、查询不存在的点返回空 name 的 Feature、在矩形区域内流式列出特征、随机生成 2~101 个点上报轨迹并接收汇总、以及发起 6 条 RouteNote 的双向流式会话。典型的客户端输出形如Getting feature for point (409146138, -746188906) name:Berkshire Valley Management Area Trail, Jefferson, NJ, USA location:latitude:409146138 longitude:-746188906 Getting feature for point (0, 0) location:latitude:0 longitude:0 Looking for features within lo:latitude:400000000 longitude:-750000000 hi:latitude:420000000 longitude:-730000000 ... Traversing 42 points. Route summary: point_count:42 feature_count:4 distance:643033 elapsed_time:0 Got message First message at point(0, 1) Got message Fourth message at point(0, 1) ...需要提醒的是本示例位于独立的 Go modulegoogle.golang.org/grpc/examples见 examples/go.mod其中通过replace google.golang.org/grpc ../将 gRPC-Go 主库替换为本地仓库源码因此在本地克隆仓库后可以直接go run运行而无需依赖远程模块。若首次运行报缺依赖在examples/目录下执行一次go mod tidy即可。可选命令行参数README 明确说明 server 与 client 都支持可选的命令行参数最典型的是 TLS。服务端与客户端的完整参数均通过 Go 标准库flag包注册下面逐一展开。服务端参数server.go 第 46-52 行参数默认值说明-tlsfalse为 true 时启用 TLS否则使用明文 TCP-cert_fileTLS 时缺省指向examples/data/x509/server_cert.pemTLS 证书文件路径-key_fileTLS 时缺省指向examples/data/x509/server_key.pemTLS 私钥文件路径-json_db_file缺省使用内嵌数据包含特征列表的 JSON 文件路径-port50051服务监听端口例如自定义端口并指定外部特征数据$ go run server/server.go -port50052 -json_db_filetestdata/route_guide_db.json客户端参数client.go 第 40-45 行参数默认值说明-tlsfalse为 true 时启用 TLS-ca_fileTLS 时缺省指向examples/data/x509/ca_cert.pemCA 根证书文件路径-addrlocalhost:50051服务端地址格式为host:port-server_host_overridex.test.example.comTLS 握手时用于校验服务端主机名的 Server Name例如客户端连接非默认端口并启用 TLS$ go run client/client.go -addrlocalhost:50052 -tlstrue启用 TLSREADME 中的两条核心命令默认情况下 server 与 client 均以明文 TCP 通信。按 README 的说明启用 TLS 只需给两端同时加上-tlstrue$ go run server/server.go -tlstrue$ go run client/client.go -tlstrue证书的加载逻辑藏在源码中服务端在tls为 true 且未显式传-cert_file/-key_file时会通过 data.Path 解析出examples/data/x509/下的server_cert.pem与server_key.pemserver.go 第 225-232 行再调用credentials.NewServerTLSFromFile构造服务端凭证客户端同理缺省使用x509/ca_cert.pem作为 CA 根证书并通过server_host_override默认x.test.example.com做主机名校验client.go 第 157-165 行。这套自签证书体系位于 examples/data/x509目录内的create.sh与openssl.cnf展示了这些测试证书的生成方式。服务端实现四个 handler 的源码级拆解server.go 中的routeGuideServer结构体嵌入了pb.UnimplementedRouteGuideServer保证新增 RPC 时旧实现仍可编译并持有两个核心状态savedFeatures []*pb.Feature只读的特征列表启动时加载routeNotes map[string][]*pb.RouteNote以经纬度字符串为键的笔记存储配sync.Mutex保护。四个方法的实现逻辑如下GetFeature一元第 63-71 行 遍历savedFeatures用proto.Equal精确比对坐标命中则返回该 Feature未命中则返回一个name为空的 Feature——这与 proto 注释中无地标时 name 为空的约定完全一致。ListFeatures服务端流式第 74-83 行 遍历特征列表通过inRange第 193-206 行判断坐标是否落在 Rectangle 内命中则stream.Send(feature)逐条下发inRange会对 lo/hi 两个对角点做min/max归一化因此两个角点传入顺序任意。RecordRoute客户端流式第 90-119 行 在for循环中不断stream.Recv()接收点收到io.EOF表示客户端已关闭发送此时计算并SendAndClose返回RouteSummary。统计过程中点与点之间的累计里程使用 Haversine半正矢公式计算calcDistance 第 174-191 行以地球半径 6371000 米为基准经纬度按1e7的 CordFactor 从 E7 整数还原为度数。RouteChat双向流式第 123-149 行 一边Recv接收客户端发来的笔记一边把该位置点此前收到的全部历史笔记Send回客户端。源码中有一段值得学习的并发细节加锁取出笔记切片后先复制一份再解锁发送注释明确说明这个拷贝是为了在服务该客户端时不阻塞其他客户端由于切片元素只增不改无需深拷贝。这正是 gRPC 流式 handler 中锁外 I/O的典型写法可避免长时间持锁。服务端的装配流程在 main 第 218-241 行net.Listen(tcp, localhost:port)监听端口 → 按需构造 TLS 凭证 →grpc.NewServer(opts...)创建服务 →pb.RegisterRouteGuideServer注册实现 →grpcServer.Serve(lis)阻塞服务。特征数据内嵌数据与 testdata JSONloadFeatures第 152-166 行负责加载特征列表若指定了-json_db_file则读取该文件否则使用源码内嵌的exampleData——该字节切片是 testdata/route_guide_db.json 的完整拷贝共约 100 条美国新泽西/纽约州地区的真实街道地址部分条目name为空表示该点无地标。内嵌数据的设计目的是避免go run时指定文件路径见源码注释。每条记录的 JSON 结构为{ location: { latitude: 407838351, longitude: -746143763 }, name: Patriots Path, Mendham, NJ 07945, USA }客户端实现五种典型调用场景client.go 的main在建立连接后依次演示了五种场景每个调用都基于context.WithTimeout设置了 10 秒超时查询已知地标printFeature查询(409146138, -746188906)命中Berkshire Valley Management Area Trail查询不存在的地标查询(0, 0)验证服务端返回空 name 的 Feature 分支矩形区域流式列出特征printFeatures请求经纬度(40, -75)至(42, -73)的 Rectangle循环stream.Recv()直到io.EOF上报随机轨迹runRecordRoute随机生成 2~101 个点rand.Int32N(100) 2保证至少两个点才能计算距离逐个stream.Send后以CloseAndRecv获取RouteSummary双向流式聊天runRouteChat发送 6 条 RouteNote同时启动一个 goroutine 循环Recv服务端回传的历史笔记最后CloseSend并通过 channel 等待接收侧结束。其中第 5 点最能体现双向流式的时序特点客户端发送的 6 条笔记中(0,1)、(0,2)、(0,3)三个位置各出现两次因此服务端回传时第二个位置的笔记到达后客户端会同时收到第一条与该位置的笔记——输出中Got message First message与Got message Fourth message同址出现的现象即源于此。连接建立方面客户端默认使用insecure.NewCredentials()明文凭证启用 TLS 时切换为credentials.NewClientTLSFromFile(caFile, serverHostOverride)。注意客户端使用的是较新的grpc.NewClient(*serverAddr, opts...)API而非已废弃的grpc.Dial这也是当前 gRPC-Go 推荐的非阻塞式建连方式。小结Route Guide 示例把 gRPC 的四种 RPC 模式压缩进一个业务场景GetFeature用于理解请求-响应模型ListFeatures演示服务端如何逐条推送数据RecordRoute展示客户端如何持续上报并在结束时收取汇总RouteChat则完整呈现了双向流式的读写并发模型。配合 README.md 的两条运行命令与-tlstrue参数你可以零成本地在本地把四种模式全部跑通而 server.go 中锁外 I/O 的拷贝发送Haversine 距离计算UnimplementedRouteGuideServer 嵌入等细节则为你编写生产级 gRPC 服务提供了可直接借鉴的范本。【免费下载链接】grpc-goThe Go language implementation of gRPC. HTTP/2 based RPC项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-go创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考