grpc-gateway 教程:用 Protocol Buffers 定义并运行一个 gRPC Hello World 服务
2026/9/13 18:25:41 网站建设 项目流程

grpc-gateway 教程:用 Protocol Buffers 定义并运行一个 gRPC Hello World 服务

【免费下载链接】grpc-gatewaygRPC to JSON proxy generator following the gRPC HTTP spec项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-gateway

本教程是 grpc-gateway 入门系列的第一篇实战文章,目标是让你从零开始、仅用一个.proto文件,定义出一个可被 gRPC 与 HTTP/JSON 双协议访问的 Hello World 服务。读完本文后,你将掌握 proto3 语法下service/message/rpc的定义方式、Greeter服务的标准写法,并能结合仓库中的真实示例与生成代码,理解 gRPC-Gateway 反向代理的生成原理,为后续添加google.api.http注解、生成 stub 与编写网关入口(main.go)打好基础。

为什么先写一个 Hello World gRPC 服务

gRPC-Gateway 不是一个独立的运行时框架,而是 Google Protocol Buffers 编译器(protoc)的一个插件:它读取.proto服务定义,根据服务中的google.api.http注解,生成一个把 RESTful HTTP API 翻译成 gRPC 调用的反向代理服务器(见 docs/docs/tutorials/introduction.md)。

也就是说,一切代码生成都从.proto文件开始。在给服务加上 HTTP 映射注解之前,我们必须先掌握如何用协议缓冲区定义出一个纯粹的、可独立运行的 gRPC 服务。这正是本文的核心:HelloRequestHelloReply两个消息,加上一个SayHello()方法,就构成了 gRPC 世界里最经典的“Hello World”。

定义 gRPC 服务:创建 hello_world.proto

按教程约定,我们在proto/helloworld/目录下创建hello_world.proto文件(路径为proto/helloworld/hello_world.proto),内容如下:

syntax = "proto3"; package helloworld; // The greeting service definition service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; }

这份文件虽然短,却包含了 proto3 的四个核心语法要素:

语法要素示例作用
syntax = "proto3";文件首行声明使用 proto3 语法版本
packagepackage helloworld;定义命名空间,避免跨文件消息名冲突
serviceservice Greeter { ... }声明一个 gRPC 服务,内部包含 RPC 方法
messagemessage HelloRequest定义请求 / 响应消息结构,字段带编号(field number)

其中rpc SayHello (HelloRequest) returns (HelloReply) {}声明了一个一元 RPC(unary RPC):客户端传入HelloRequest,服务端返回HelloReply。字段后的数字= 1是字段编号,在 proto3 中用于二进制编码标识,一旦发布就不应更改。

对于 proto3 语法细节(如字段类型、重复字段、嵌套消息等)的深入讲解,可以参考 Google Protocol Buffers 官方文档及其 Go 语言入门教程;对本系列而言,你只需理解:服务端和客户端 stub 都会拥有一个SayHello()方法,入参是HelloRequest,出参是HelloReply,这与后续在 Go 代码中实现该接口的方式一一对应。

从仓库实例看完整的 proto 定义:注解、类型与附加绑定

教程中的hello_world.proto是刻意精简的教学版本。而在 grpc-gateway 仓库内部,examples/internal/helloworld/helloworld.proto提供了一个加了 gRPC-Gateway 注解的真实版本,能让你提前看到从纯 gRPC 到 HTTP/JSON 的差距:

syntax = "proto3"; package grpc.gateway.examples.internal.helloworld; import "google/api/annotations.proto"; import "google/protobuf/wrappers.proto"; option go_package = "github.com/grpc-ecosystem/grpc-gateway/v2/examples/internal/helloworld"; service Greeter { rpc SayHello(HelloRequest) returns (HelloReply) { option (google.api.http) = { get: "/say/{name}" additional_bindings: {get: "/say/strval/{strVal}"} additional_bindings: {get: "/say/floatval/{floatVal}"} additional_bindings: {get: "/say/boolval/{boolVal}"} additional_bindings: {get: "/say/int64val/{int64Val}"} }; } } message HelloRequest { string name = 1; google.protobuf.StringValue strVal = 2; google.protobuf.FloatValue floatVal = 3; google.protobuf.DoubleValue doubleVal = 4; google.protobuf.BoolValue boolVal = 5; google.protobuf.BytesValue bytesVal = 6; google.protobuf.Int32Value int32Val = 7; google.protobuf.UInt32Value uint32Val = 8; google.protobuf.Int64Value int64Val = 9; google.protobuf.UInt64Value uint64Val = 10; } message HelloReply { string message = 1; }

(为简洁起见,上面示例省略了仓库原文件中的部分additional_bindings,完整内容请直接查看 examples/internal/helloworld/helloworld.proto。)

这个真实示例相比教程版本多出三个关键点,它们正是下一阶段“添加注解”的核心内容:

  1. import "google/api/annotations.proto":只有引入了该文件,才能在 RPC 上使用google.api.http注解。这是 gRPC-Gateway 识别 HTTP 映射的唯一入口。
  2. option (google.api.http) = { get: "/say/{name}" ... }:把SayHello映射到GET /say/{name}{name}花括号语法表示从 URL 路径中提取名为name的参数并填入HelloRequest.nameadditional_bindings则允许同一个 RPC 绑定多条额外的 HTTP 路径。
  3. wrapper 类型字段google.protobuf.StringValueFloatValue等包装类型(wrappers)允许在 JSON 中表达“字段是否存在”与“值为 null”的语义,examplepb等其它示例 proto 文件中也有类似用法。

生成代码长什么样:以仓库的 helloworld.pb.gw.go 为例

定义好.proto并运行生成器后,会得到若干*.pb.go*.pb.gw.go文件。仓库中已经提交了生成产物,可以直接阅读来验证“proto 定义 → 代码”的映射关系:

  • examples/internal/helloworld/helloworld.pb.go:消息类型HelloRequest/HelloReply的 Go 结构体与编解码实现;
  • examples/internal/helloworld/helloworld_grpc.pb.goGreeterServer服务端接口与GreeterClient客户端 stub;
  • examples/internal/helloworld/helloworld.pb.gw.go由 protoc-gen-grpc-gateway 生成的“反向代理”代码,文件头注释明确写着Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.,包注释为 “It translates gRPC into RESTful JSON APIs.”。

helloworld.pb.gw.go为例,可以清楚看到 HTTP 请求是如何被翻译成 gRPC 调用的(见 examples/internal/helloworld/helloworld.pb.gw.go):

func request_Greeter_SayHello_0(ctx context.Context, marshaler runtime.Marshaler, client GreeterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { // ... val, ok := pathParams["name"] if !ok { return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") } protoReq.Name, err = runtime.String(val) // ... }

关键点在于pathParams["name"]:它正是从GET /say/{name}的路径模板中解析出来的name参数,随后被runtime.String(val)转换为字符串并填入HelloRequest.Name。可见路径参数到 gRPC 请求消息的填充逻辑完全是由生成器自动产出的,开发者无需手写任何 HTTP 解析代码。

再看服务注册入口(同上文件约 804 行附近):

func RegisterGreeterHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { return RegisterGreeterHandlerClient(ctx, mux, NewGreeterClient(conn)) }

它接受一个已拨通的grpc.ClientConn,把 HTTP handler 注册到runtime.ServeMux上。这个函数正是下一篇教程(docs/docs/tutorials/creating_main.go.md)中main.go里调用的helloworldpb.RegisterGreeterHandler(...)——到时你会看到完整的调用链:HTTP 请求 → ServeMux → 生成的反向代理 → gRPC ClientConn → gRPC 服务端

从 Hello World 到 gRPC-Gateway:本教程在整个系列中的位置

按官方教程的导航顺序(docs/docs/tutorials/index.md),本系列共分五步,本文是第一站:

  1. 本文:用 proto 定义Greeter/SayHello,先让纯 gRPC 服务跑通;
  2. generating_stubs/index.md:用protocbuf生成 Go stub;
  3. creating_main.go.md:编写 Go gRPC 服务端main.go,实现SayHello并注册到 gRPC server;
  4. adding_annotations.md:给SayHello添加google.api.http注解(如post: "/v1/example/echo"body: "*"),再生成*.gw.pb.go并注册 gRPC-Gateway mux;
  5. learn_more.md:深入学习后续内容。

也就是说,本文定义的 proto 文件在后续步骤中会被反复修改和重新生成:先加 HTTP 注解,再生成 stub,最后用go run main.go启动双协议服务,并用curl -X POST http://localhost:8090/v1/example/echo -d '{"name":" hello"}'得到{"message":"hello world"}的 JSON 响应。整个过程印证了 gRPC-Gateway 的设计哲学:只在.proto文件中编写一次服务定义,就能同时获得 gRPC 与 HTTP/JSON 两种 API(出处:docs/docs/tutorials/introduction.md)。

动手前的准备:前置工具与 go.mod 初始化

虽然本文只涉及 proto 定义,但为了后续步骤能顺利执行,建议先按 docs/docs/tutorials/introduction.md 完成环境准备:

  1. 安装 Go(教程示例使用 Go 编写 gRPC 服务);
  2. 安装三个 protoc 生成器插件(需确保$GOPATH/bin$PATH中):
$ go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
  1. 在工作目录初始化模块:
$ go mod init github.com/myuser/myrepo go: creating new go.mod: module github.com/myuser/myrepo

教程使用github.com/myuser/myrepo作为模块路径占位符;实际生产代码中应替换为你的模块可被下载的 URL。后续main.go中会以helloworldpb "github.com/myuser/myrepo/proto/helloworld"的形式导入本教程生成的包(见 docs/docs/tutorials/creating_main.go.md)。

小结与下一步

通过本文,你已经完成了 gRPC-Gateway 入门的第一步:

  • 掌握 proto3 中service/rpc/message的基本定义语法;
  • 理解HelloRequest(入参)与HelloReply(出参)在服务端、客户端 stub 中的对应关系;
  • 通过仓库中的 examples/internal/helloworld/helloworld.proto 与 examples/internal/helloworld/helloworld.pb.gw.go,提前看到注解、wrapper 类型与反向代理生成代码的全貌。

接下来,请进入系列第二步 生成 stub(generating_stubs),选择protocbuf其中一种方式,把本文件定义的Greeter服务编译为 Go 代码;随后按 creating_main.go.md 编写服务端实现,再按 adding_annotations.md 为SayHello挂上 HTTP 映射,一个同时支持 gRPC 与 HTTP/JSON 的 Hello World 服务就真正跑起来了。

【免费下载链接】grpc-gatewaygRPC to JSON proxy generator following the gRPC HTTP spec项目地址: https://gitcode.com/GitHub_Trending/gr/grpc-gateway

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询