gRPC和gRPC-gateway

为什么选择gRPC和gRPC-gateway

人应该积极,但牛马应当消极,在能偷懒的地方决不含糊。

gRPC

protoc 编译.proto github.com/protocolbuf...
protoc-gen-go 转译Go数据结构 github.com/protocolbuf...
protoc-gen-go-grpc 生成Go服务代码 github.com/grpc/grpc-g...

gRPC-gateway

protoc-gen-grpc-gateway 生成http反向代理 github.com/grpc-ecosys...
protoc-gen-openapiv2 生成接口文档 github.com/grpc-ecosys...
buf 编译.proto github.com/bufbuild/bu...
protoc-gen-buf-lint linter github.com/bufbuild/bu...
protoc-gen-buf-breaking 兼容性检查 github.com/bufbuild/bu...
  • 每个仓库下的 Releases 标签内含有其 binary 文件,下载即可

  • 按照仓库各自的 Readme 编译安装亦可

从零开始的工程

按照 project-layout 指引建立适合的简化项目结构,后续在需要的时候逐步补上即可

bash 复制代码
├── api        # 服务接口, 例如 http, grpc, api文档
├── build      # 部署脚本, 例如编译脚本
├── cmd        # app启动文件
├── configs    # 配置文件
├── internal   # 工程相关编码文件, 业务代码
└── pkg        # 通用代码, 例如公用的库

什么是gRPC-gateway?

按照官方的说法,gateway可以同时暴露 gRPC 和 RESTful HTTP 的 API 提供服务,先上图

什么是RPC?

假设在代码中有一个打印 Hello World! 的 echo 函数,本地调用的代码是长这样的:

go 复制代码
package main

import "fmt"

func main() {
    fmt.Println(echo("Hello, World!"))
}

func echo(request string) (response string) {
    return request
}

现在假设本地的机器的IP是 192.168.1.100 ,另一台IP是 192.168.1.101 的机器充当服务器,如果有方式能把echo函数放到服务器中运行,然后能以类似本地调用的方式去使用:

go 复制代码
func main() {
  // 调用在服务器上的echo函数
  // 格式: IP + Port + 函数名 + 实参
  response := 192.168.1.100:8080["echo"]["Hello, World!"]
  fmt.Println(response)
}

对于使用方来说,把远端的代码称为服务,实参称为请求参数,返回值称为响应,现在使用格式:(服务器地址+服务名+请求参数)进行调用获取返回响应,这个过程就是RPC

什么是proto?

从上面知道rpc的调用格式(服务名+请求参数)-> 响应,proto就是用来描述这种格式的,如果把调用echo服务的过程翻译成proto,代码是长这样的:

protobuf 复制代码
syntax = "proto3";

message EchoRequest {
  string value = 1;
}

message EchoResponse {
  string value = 1;
}

service EchoService {
  rpc Echo(EchoRequest) returns (EchoResponse) {}
}
  • EchoRequest:请求参数,相当于Echo函数的实参

  • EchoResponse:服务响应,相当于Echo函数的返回值

  • EchoService:Echo函数本体

go 复制代码
// 调用的伪代码
func main() {
  // 格式:IP + Port + 服务名 + API + 请求参数
  response := 192.168.1.101:8080["EchoService"]["Echo"]["Hello, World!"]
}

对于用proto描述的API,我们期望以这样的形式去使用:(服务器地址+服务名+API+请求参数)-> 服务响应

回到gRPC-gateway

假设你是调用者,现在需要通过HTTP的方式请求Echo服务,从发出请求到返回响应,大致是这样的:

请求 => 过滤器链(流量控制,鉴权,日志,异常处理...)=> url路由表 => 请求参数验证 => 具体服务 => 返回响应

对于gRPC的服务也是同理,流程还需要同样来一遍,意味着要维护流程一致,只是协议不同的两个服务

gRPC-gateway做了什么?

通过protobuf的自定义option实现了一个网关,服务端同时开启gRPCHTTP 1.1服务,HTTP服务接收客户端请求后转换为grpc请求数据,获取响应后转为json数据返回给客户端。

什么意思?

  • proto描述了gRPC请求参数,api服务,返回参数,定义了使用格式

  • proto还能配合一些额外自定义配置描述HTTP的url

  • gateway能把HTTP的url映射到gRPC的具体服务一一对应起来

gRPC-gateway就是通过代码生成的方式把proto描述的服务翻译成HTTP服务的接口层和gRPC的接口层。

还是以echo服务为例,请求url是:POST /v1/example/echo,请求body是:"Hello, World!",到达HTTP服务时,HTTP处理过程中会把自己伪装成gRPC客户端,把HTTP的请求参数转换成gRPC请求格式,查询url对应到gRPC服务签名是rpc Echo(EchoRequest) returns (EchoResponse),转发到gRPC的具体服务处理返回响应,把从gRPC返回的响应包装成HTTP需要的json数据,最后返回给调用者。

这样做有什么用?

一般对于现在的系统架构来说,服务器与服务器之间是通过RPC交互,对外提供服务通过HTTP-JSON交互,基于这个前提,同时维护HTTP和GRPC是件很痛苦的事情。

  • 基础痛苦:HTTP服务和gRPC的过滤器链大多数都是重复的,同样的逻辑代码要码两次。

  • 双倍痛苦:API需要修改请求和响应参数的时候,同样要修改两次,同理还有API文档。

  • 三倍痛苦:假设系统是微服务架构时,多个服务由多语言编写,接口层的工作量巨大。

当不巧所有工作都是由你负责,包括还有前端的时候,面具得戴上。

如何使用?代码怎么写?

  • 编写.proto文件,描述请求参数,api服务,返回参数

  • 在.proto文件中引入http.proto,描述HTTP的url如何映射到gRPC服务

  • 在使用http.proto时,同时指定HTTP的请求参数如何转换到gRPC的请求参数

  • 通过protoc或者buf配合protoc-gen-go,protoc-gen-go-grpc,protoc-gen-grpc-gateway编译.proto生成HTTP和gRPC接口层代码

  • 根据.proto定义的api服务签名,实现业务逻辑

  • 实例化gRPC服务绑定ip+port

  • 实例化HTTP服务绑定ip+port,并伪装成gRPC客户端

相关推荐
NPE~9 小时前
Bug:Goland debug失效详细解决步骤【合集】
go·bug·goland·dlv失效
喵个咪17 小时前
开箱即用的GO后台管理系统 Kratos Admin - 交互式API文档 Swagger UI
后端·go·swagger
小石潭记丶1 天前
goland无法debug项目
go
千舟1 天前
自己动手编写tcp/ip协议栈4:tcp数据传输和四次挥手
网络协议·go
喵个咪3 天前
开箱即用的GO后台管理系统 Kratos Admin - 后端项目结构说明
后端·微服务·go
烛阴3 天前
Go语言内置包:提升开发效率的必备神器!
后端·go
天葬4 天前
Ollama 模型迁移备份工具 ollamab
go·ollamab
魔法小匠4 天前
「Go设计哲学」为什么云原生时代属于Go语言?(剖析Go核心优势)
go
陈明勇5 天前
Go 1.24 新特性:泛型类型别名,让代码变得更灵活、更清晰
后端·go