Golang 语言 gRPC 服务怎么同时支持 gRPC 和 HTTP 客户端调用?

简介: Golang 语言 gRPC 服务怎么同时支持 gRPC 和 HTTP 客户端调用?

01

介绍

关于 gRPC 的文章,我们之前写过几篇,如果读者朋友还对 gRPC 不了解,我建议您可以翻阅一下公众号的历史文章。

当我们需要提供 gRPC 服务的 RESTful API 时,可以先创建一个 gRPC 客户端服务,在 gRPC 客户端服务编写 RESTful API,接收到 HTTP 请求时,通过 gRPC 客户端服务调用 gRPC 服务端服务的方法。

相信读者朋友们也意识到,仅仅为了提供 RESTful API 而编写一个 gRPC 客户端服务,显然有些小题大做。

在不借助 gRPC 客户端服务的前提下,gRPC 服务端服务怎么同时支持 gRPC 和 HTTP 客户端调用?今天我们介绍一个 protoc 插件 gRPC-Gateway。

gRPC-Gateway

gRPC-Gateway 是 protoc 的一个插件。它读取 gRPC 服务定义并生成一个反向代理服务器,该服务器将 RESTful JSON API 转换为 gRPC。此服务器是根据 gRPC 定义中的自定义选项生成的。

gRPC-Gateway 可帮助您同时以 gRPC 和 RESTful 风格提供 API。

在我们开始编码之前,需要一些先决条件。

  1. 首先,我们需要先搭建一个 Go 环境。
  2. 使用 go get 工具下载一些依赖包。
  3. 使用 go mod init 工具创建一个 go.mod 文件。

依赖包列表:

$ go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
$ go get google.golang.org/protobuf/cmd/protoc-gen-go
$ go get google.golang.org/grpc/cmd/protoc-gen-go-grpc

03

gRPC-Gateway 实战

在完成以上先决条件后,我们创建一个 gRPC 服务端服务,本文我们创建一个 ToDoList gRPC 服务。在创建 gRPC 服务之前,我们使用 protocol buffers 创建一个 proto 文件。

创建 proto 文件

...
service ToDoList {
  rpc CreateToDoList (ToDoListDetail) returns (CreateToDoListResult) {}
  rpc ReadToDoList (ToDoListPage) returns (ReadToDoListByPage) {}
}
...

生成 gRPC 服务端存根

使用 protoc 命令工具生成存根

protoc -I proto \
   --go_out ./pb/todoPb --go_opt paths=source_relative \
   --go-grpc_out ./pb/todoPb --go-grpc_opt paths=source_relative \
   proto/toDoList.proto

执行上面 protoc 命令工具,生成一个 *.pb.go 文件和一个 *_grpc.pb.go 文件。

编写剩余 Go 代码

创建 main.go

func main() {
 InitEngine()
 lis, err := net.Listen("tcp", address)
 if err != nil {
  log.Fatalf("failed to listen: %v", err)
 }
 server := grpc.NewServer()
 pb.RegisterToDoListServer(server, new(service.ToDoList))
 log.Printf("server listening at %v\n", lis.Addr())
 if err := server.Serve(lis); err != nil {
  log.Fatalf("failed to serve: %v", err)
 }
}

完整代码,请参阅 Github。

添加 gRPC-Gateway 选项

gRPC-Gateway 使用 google.api.http 选项定义 gRPC 服务如何映射到 JSON 请求和响应,使用 protoc 时,每个 RPC 必须使用 google.api.http 选项定义 HTTP 方法和路径。

因此,我们需要将 google/api/http.proto 导入添加到 proto 文件中。我们还需要添加我们想要的 HTTP -> gRPC 映射。

syntax = "proto3";
import "google/api/annotations.proto";
service ToDoList {
  rpc CreateToDoList (ToDoListDetail) returns (CreateToDoListResult) {
    option (google.api.http) = {
      post: "/v1/todolist/add"
      body: "*"
    };
  }
  rpc ReadToDoList (ToDoListPage) returns (ReadToDoListByPage) {
    option (google.api.http) = {
      get: "/v1/todolist/select"
    };
  }
}
...

关于 HTTP 和 gRPC 映射的更多内容,可以参阅 Google API 文档。

生成 gRPC-Gateway 存根

现在,我们已将 gRPC-Gateway 选项添加到 proto 文件中,我们需要使用 gRPC-Gateway 生成器来生成存根。

在使用 protoc 生成存根之前,我们需要将一些依赖项复制到 proto 文件目录中。将 googleapis 的子集从官方存储库下载并复制到本地 proto 文件目录中。如下所示:

.
├── dao
│   ├── mysql.go
│   └── toDoList.go
├── grpc-gateway
│   └── main.go
├── main.go
├── pb
│   └── todoPb
│       ├── toDoList.pb.go
│       ├── toDoList.pb.gw.go
│       └── toDoList_grpc.pb.go
├── proto
│   ├── google
│   │   └── api
│   │       ├── annotations.proto
│   │       └── http.proto
│   └── toDoList.proto
└── service
    └── toDoList.go

使用 protoc 生成存根

protoc -I proto \
  --go_out ./pb/todoPb --go_opt paths=source_relative \
  --go-grpc_out ./pb/todoPb --go-grpc_opt paths=source_relative \
  --grpc-gateway_out ./pb/todoPb --grpc-gateway_opt paths=source_relative \
  proto/toDoList.proto
protoc-go-inject-tag -XXX_skip=xorm -input=./pb/todoPb/toDoList.pb.go

执行以上 protoc 命令工具,生成一个 *.gw.pb.go 文件。

创建 grpc-gateway 目录,并创建 main.go 文件,创建 gRPC-Gateway 多路复用器。

func main() {
 ctx := context.Background()
 ctx, cancel := context.WithCancel(ctx)
 defer cancel()
 mux := runtime.NewServeMux()
 opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
 err := pb.RegisterToDoListHandlerFromEndpoint(ctx, mux, grpcServerEndpoint, opts)
 if err != nil {
  log.Fatalf("Fail to register gRPC gateway service endpoint: %v", err)
 }
 if err = http.ListenAndServe(":8080", mux); err != nil {
  log.Fatalf("Could not setup HTTP endpoint: %v", err)
 }
}

完整代码,请参阅 Github。

启动服务

grpc 服务

go run main.go

gRPC-Gateway

go run grpc-gateway/main.go

cURL 测试

curl http://127.0.0.1:8080/v1/todolist/select?page=1&count=2

响应结果:

{
    "todolist": [
        {
            "id": "1",
            "content": "编程写代码",
            "datetime": "1632541505",
            "created": "1632541505",
            "updated": "1632541505"
        },
        {
            "id": "2",
            "content": "编程写代码",
            "datetime": "1632543373",
            "created": "1632543373",
            "updated": "1632543373"
        }
    ]
}

04

总结

本文我们介绍 gRPC-Gateway 如何实现同时支持 gRPC 和 RESTful 风格的 API。

当 HTTP 请求到达 gRPC-Gateway 时,它会将 JSON 数据解析为 protobuf 消息。然后,它使用解析的 protobuf 消息发出正常的 Go gRPC 客户端请求。

Go gRPC 客户端将 protobuf 结构编码为 protobuf 二进制格式,并将其发送到 gRPC 服务器。gRPC 服务器处理请求并以 protobuf 二进制格式返回响应。

Go gRPC 客户端将其解析为 protobuf 消息,并将其返回到 gRPC-Gateway,后者将 protobuf 消息编码为 JSON 并将其返回到原始客户端。

图片来自 gRPC-Gateway 官方文档

推荐阅读:

Golang 语言 gRPC 怎么使用?

参考资料:

https://github.com/grpc-ecosystem/grpc-gateway 

https://github.com/googleapis/googleapis 

https://github.com/googleapis/googleapis.github.io/ 


目录
相关文章
|
11月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
495 2
|
JSON 中间件 Go
Go 网络编程:HTTP服务与客户端开发
Go 语言的 `net/http` 包功能强大,可快速构建高并发 HTTP 服务。本文从创建简单 HTTP 服务入手,逐步讲解请求与响应对象、URL 参数处理、自定义路由、JSON 接口、静态文件服务、中间件编写及 HTTPS 配置等内容。通过示例代码展示如何使用 `http.HandleFunc`、`http.ServeMux`、`http.Client` 等工具实现常见功能,帮助开发者掌握构建高效 Web 应用的核心技能。
650 61
|
Java Maven Windows
使用Java创建集成JACOB的HTTP服务
本文介绍了如何在Java中创建一个集成JACOB的HTTP服务,使Java应用能够调用Windows的COM组件。文章详细讲解了环境配置、动态加载JACOB DLL、创建HTTP服务器、实现IP白名单及处理HTTP请求的具体步骤,帮助读者实现Java应用与Windows系统的交互。作者拥有23年编程经验,文章来源于稀土掘金。著作权归作者所有,商业转载需授权。
563 2
使用Java创建集成JACOB的HTTP服务
|
应用服务中间件 网络安全 数据安全/隐私保护
网关服务器配置指南:实现自动DHCP地址分配、HTTP服务和SSH无密码登录。
哇哈哈,道具都准备好了,咱们的魔术秀就要开始了。现在,你的网关服务器已经魔法满满,自动分配IP,提供网页服务,SSH登录如入无人之境。而整个世界,只会知道效果,不会知道是你在幕后操控一切。这就是真正的数字世界魔法师,随手拈来,手到擒来。
665 14
|
中间件 Go
Golang | Gin:net/http与Gin启动web服务的简单比较
总的来说,`net/http`和 `Gin`都是优秀的库,它们各有优缺点。你应该根据你的需求和经验来选择最适合你的工具。希望这个比较可以帮助你做出决策。
765 35
|
关系型数据库 MySQL PHP
源码编译安装LAMP(HTTP服务,MYSQL ,PHP,以及bbs论坛)
通过以上步骤,你可以成功地在一台Linux服务器上从源码编译并安装LAMP环境,并配置一个BBS论坛(Discuz!)。这些步骤涵盖了从安装依赖、下载源代码、配置编译到安装完成的所有细节。每个命令的解释确保了过程的透明度,使即使是非专业人士也能够理解整个流程。
562 18
|
网络协议 安全 Go
Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
【10月更文挑战第28天】Go语言进行网络编程可以通过**使用TCP/IP协议栈、并发模型、HTTP协议等**方式
467 13
|
Prometheus Cloud Native Go
Golang语言之Prometheus的日志模块使用案例
这篇文章是关于如何在Golang语言项目中使用Prometheus的日志模块的案例,包括源代码编写、编译和测试步骤。
433 4
Golang语言之Prometheus的日志模块使用案例
|
前端开发 中间件 Go
实践Golang语言N层应用架构
【10月更文挑战第2天】本文介绍了如何在Go语言中使用Gin框架实现N层体系结构,借鉴了J2EE平台的多层分布式应用程序模型。文章首先概述了N层体系结构的基本概念,接着详细列出了Go语言中对应的构件名称,包括前端框架(如Vue.js、React)、Gin的处理函数和中间件、依赖注入和配置管理、会话管理和ORM库(如gorm或ent)。最后,提供了具体的代码示例,展示了如何实现HTTP请求处理、会话管理和数据库操作。
439 1
|
关系型数据库 MySQL 数据库
vertx 的http服务表单提交与mysql验证
本文介绍了如何使用Vert.x处理HTTP服务中的表单提交,并通过集成MySQL数据库进行验证,包括项目依赖配置、表单HTML代码和完整的Vert.x服务代码。
254 2

推荐镜像

更多