1、概述
gRPC常用于服务端之间的相互调用,如果想把服务暴露给前端,虽然动手修改服务端也能实现,但似乎增加了不少工作量,此时还可以选择gRPC-Gateway方式来快速将gRPC服务以http的方式暴露出来;
gRPC-Gateway 是 Google protocol buffers compiler protoc 的插件。 它读取 protobuf service 定义并生成反向代理服务器( reverse-proxy server) ,这个反向代理运行起来后,对外提供RESTful服务,收到RESTful请求后通过gRPC调用原来的gRPC服务。该服务器是根据服务定义中的 google.api.http 批注(annotations)生成的,gRPC-Gateway原理如下图:
本文展示了gRPC-Gateway v2环境搭建、开发、验证的整个过程,由以下步骤组成:
- 安装gRPC-Gateway v2插件;
- 编写proto文件;
- 根据proto文件生成gRPC、gRPC-Gateway源码;
- 添加业务代码;
- 编译、运行、验证;
2、环境配置
2.1 安装配置protocol buffers和protoc-gen-go
步骤参见:Mac下安装配置Protocol Buffers
2.2 安装配置gRPC
步骤参见:Golang gRPC概述及入门示例
2.3 安装配置gRPC-Gateway插件
下载当前最新稳定版本的gRPC-Gateway v2插件
3、gRPC-Gateway示例
在开始开发之前,先说说我们的目标。
在这个grpc-gateway-practice
项目中,我希望实现一个功能,客户端通过restful接口发送消息给网关,然后网关作为gRPC客户端将消息代理到gRPC服务端,gRPC服务端收到消息后返回响应给网关,然后网关再将消息返回给客户端。
项目结构如下:
注意: 这是整个项目所有文件生成完后的结构,所有.proto和.go文件都是在3.1及其后步骤生成的,go.mod内容如下:
3.1 定义服务
正如前面所说的,在开发server
与client
之前,我们需要先定义服务。这里直接粘贴下示例proto文件内容。
很容易可以看出,我们定义了一个service,称为MessageSender
,这个服务中有一个rpc方法,名为Send
。这个方法会发送一个MessageRequest
,然后返回一个MessageResponse
。
注意观察message.proto文件内容,和之前rpc服务的proto文件相比rpc服务方法里面多了如下代码片段:
此代码片段为 gRPC-Gateway 批注,批注定义了 gRPC 服务如何映射到 JSON 请求和响应。
使用 protocol buffers 时,每个 RPC 必须使用 google.api.http 批注定义 HTTP 方法和路径。因此,我们需要将 google/api/annotations.proto 导入添加到 proto 文件中,google/api/annotations.proto文件引自于github.com/googleapis/googleapis包。
接着在grpc-gateway-practice/pkg/proto目录下执行如下命令:
这三条命令会grpc-gateway-practice/pkg/pb目录中分别生成message.pb.go、message_grpc.pb.go、message.pb.gw.go这三个文件。在这三个文件中,包含了我们定义方法的go语言实现,也包含了我们定义的请求与相应的go语言实现。
当然也可以一条命令就把这三个文件生成出来:
3.2 服务端
3.2.1 实现服务定义的方法
很容易可以看出,MessageSenderServerImpl实现了MessageSenderServer接口,并实现定义。也就是说,这一部分是需要我们在Server
端实现这个send方法的。
3.2.2 gRPC服务端注册定义的服务并监听
很容易可以看出,我们在这一部分创建了一个grpcServer,然后注册了我们的Service,在注册函数的第二个参数中,我们传进去了一个MessageSenderServerImpl实例。
监听过程跟golang的web服务器是很像的,也是创建Handler,然后对端口进行监听,监听8002
端口的TCP连接,然后启动服务器。
至此,服务端开发完毕。
3.3 gRPC-Gateway(反向代理Reverse Proxy)
以上代码需要注意以下三点:
- 第一处要注意的地方,是调用http.ListenAndServe监听9090端口,这是对外提供RESTful服务的端口;
- 第二处要注意的地方,是echoEndpoint配置了将外部RESTful请求转发到server.go提供gRPC服务的入口处;
- 第三处要注意的地方,是调用了自动生成代码中的RegisterMessageSenderHandlerFromEndpoint方法完成上下游调用的绑定;
4、验证
分别将gRPC服务端和gRPC-Gateway都跑起来,然后,我们使用 cURL
发送 HTTP
请求:
收到响应如下,这是来自gRPC服务端的内容,可见http请求通过Reserve Proxy到达了真实的gRPC服务提供者,并顺利返回给调用方:
去看gRPC服务端的日志如下:
至此,gRPC-Gateway示例成功。