【gRPC-gateway】初探grpc网关,插件安装,默认实现,go案例

grpc-gateway

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

作用

  1. 通过反向代理的方式,将grpc server接口转为http+json api

使用场景

  1. 向后兼容
  2. 支持grpc不支持的语言或客户端
  • 单纯用grpc实现的服务端代码,只能用grpc客户端调用,(比如用 gRPC 官方提供的 Go、Python、Java 等 SDK 进行调用)现实开发中,不是所有客户端都支持 gRPC,比如:

    • 浏览器(Web 前端):大部分浏览器不支持 gRPC,虽然有 gRPC-Web 方案,但仍然有兼容性问题。
    • 传统 HTTP 客户端(如 curl、Postman、前端 AJAX 请求):这些工具一般都使用 HTTP/JSON,而不是 gRPC 的 Protobuf。
    • 外部系统(第三方 API 调用):很多外部服务只能通过 HTTP 调用,无法直接对接 gRPC。

gRPC 和 gRPC-Gateway 的核心区别

对比项 直接使用 gRPC 使用 gRPC-Gateway
协议 gRPC(HTTP/2 + Protobuf) HTTP/JSON 转 gRPC
客户端支持 仅限 gRPC 客户端(Go、Python、Java...) 兼容 HTTP 客户端(curl、浏览器、前端)
调用方式 需要 gRPC Stub(代码生成的客户端) 直接使用 HTTP/JSON
网络环境 需要支持 HTTP/2(可能受限于代理、网络设备) 兼容 HTTP/1.1,适用于任何 HTTP 设备

什么时候不需要 gRPC-Gateway?

  • 如果你的服务只给 gRPC 客户端用(比如后端微服务之间的通信),那就没必要加 gRPC-Gateway,直接 gRPC 通信性能更好。
  • 如果你能确保所有客户端都支持 gRPC,例如你的团队只开发 gRPC 客户端,那就不需要 HTTP 网关。

总结

gRPC-Gateway 主要用于 让 gRPC 服务同时支持 HTTP/JSON 调用,方便 Web 前端、第三方系统或传统 REST 客户端对接。如果你的 gRPC 服务只在后端内部使用,不需要对外提供 REST API,那就可以不用 gRPC-Gateway。


安装插件 protoc-gen-grpc-gateway

bash 复制代码
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest

检查版本

bash 复制代码
protoc-gen-grpc-gateway --version
插件的作用
  • 代码生成 :根据 .proto 文件中的 HTTP 注解,生成反向代理代码 .pb.gw.go

  • 协议转换:将 HTTP 请求映射到对应的 gRPC 方法,并处理参数和响应转换。


echo.proto

proto 复制代码
syntax = "proto3";
package echo;
option go_package = "echo/proto";

message User{
  int64 id = 1;
  string name = 2;
  int32 age = 3;
  string phone = 4;
  Addr addr = 5;
}

message Addr{
  string province = 1;
  string city = 2;
  string county = 3;
}

// 在 gRPC-Gateway 中,HTTP 方法(如 GET 和 POST)的映射是由 grpc-gateway 生成的代码和 .proto 文件中的选项配置决定的。
// 具体来说,grpc-gateway 使用 .proto 文件中的 google.api.http 选项来定义每个 gRPC 方法对应的 HTTP 方法和路径。
// 当 grpc-gateway 没有找到 google.api.http 选项时,它会根据 gRPC 方法的签名(即方法名和参数类型)生成默认的 HTTP 映射规则。
// GET 请求:如果 gRPC 方法只接收一个请求消息并且返回一个响应消息,并且请求消息中的字段可以作为 URL 路径参数或查询参数,则 grpc-gateway 会为该方法生成一个 GET 请求。
// POST 请求:对于所有其他情况,默认生成 POST 请求。

// 这里则都默认生成post而不生成get
// 路径参数:适合唯一标识符,不宜包含多个字段或复杂结构。
// 查询参数:适合少量的过滤条件或附加信息,不宜包含复杂数据结构。
// User 消息:包含多个字段和嵌套结构,不适合直接映射为路径参数或查询参数,
// 因此 grpc-gateway 默认选择 POST 请求以确保完整性和灵活性。
service Echo{
  rpc Get(User) returns (User) {}
  rpc AddOrUpdate(User) returns (User) {}
  rpc Delete(User) returns (User) {}
}
1. 核心模块
1.1 main

功能:应用程序的入口点,负责启动 gRPC 服务器和 gRPC-Gateway。

1.1.1 main.go

功能 :启动 gRPC 服务器和 gRPC-Gateway,并处理信号以优雅地关闭服务器。
关键点

  • 并发启动:使用两个 goroutine 分别启动 gRPC 服务器和 gRPC-Gateway。
  • gRPC 服务器 :通过 run() 函数创建并启动 gRPC 服务器,监听在 :50051 端口。
  • gRPC-Gateway :通过 gateway.Run() 启动 HTTP 服务器,监听在 :8081 端口。
  • 信号处理 :使用 signal.NotifyContext 监听中断信号(如 Ctrl+C),以便优雅地关闭服务器。
go 复制代码
func main() {
	go func() {
		if err := run(); err != nil {
			log.Fatalln(err)
		}
	}()
	time.Sleep(time.Second)
	go func() {
		if err := gateway.Run(); err != nil {
			log.Fatalln(err)
		}
	}()

	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
	defer stop()
	<-ctx.Done()
}

func run() error {
	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalln(err)
	}
	s := grpc.NewServer()
	userServiceServer := server.NewServer()
	proto.RegisterEchoServer(s, userServiceServer)
	return s.Serve(lis)
}
1.2 server

功能:实现了 gRPC 服务的具体逻辑。

1.2.1 server.go

功能 :定义了 echoServer 结构体及其方法实现。
关键点

  • echoServer 结构体 :嵌套了 UnimplementedEchoServer,确保未实现的方法不会导致 panic。通过值嵌入而不是指针嵌入,避免 nil 指针解引用问题。
  • NewServer 函数 :创建并返回一个新的 EchoServer 实例。
  • 方法实现Get, AddOrUpdate, Delete 方法简单地打印传入的 User 对象并返回它。
go 复制代码
type echoServer struct {
	proto.UnimplementedEchoServer
}

func NewServer() proto.EchoServer {
	return &echoServer{}
}

func (s *echoServer) Get(ctx context.Context, in *proto.User) (*proto.User, error) {
	fmt.Printf("#{in}\n")
	return in, nil
}

func (s *echoServer) AddOrUpdate(ctx context.Context, in *proto.User) (*proto.User, error) {
	fmt.Printf("#{in}\n")
	return in, nil
}

func (s *echoServer) Delete(ctx context.Context, in *proto.User) (*proto.User, error) {
	fmt.Printf("#{in}\n")
	return in, nil
}
1.3 proto

功能:定义了 gRPC 服务的消息格式和服务接口。

1.3.1 echo.proto

功能 :定义了 UserAddr 消息以及 Echo 服务。
关键点

  • 消息定义UserAddr 消息定义了用户和地址的数据结构。
  • 服务定义Echo 服务定义了三个 RPC 方法:Get, AddOrUpdate, Delete,每个方法接收和返回 User 消息。
  • HTTP 映射规则 :默认情况下,由于 User 消息包含多个字段和嵌套结构,不适合直接映射为路径参数或查询参数,因此 grpc-gateway 默认选择 POST 请求以确保完整性和灵活性。
proto 复制代码
syntax = "proto3";
package echo;
option go_package = "echo/proto";

message User {
  int64 id = 1;
  string name = 2;
  int32 age = 3;
  string phone = 4;
  Addr addr = 5;
}

message Addr {
  string province = 1;
  string city = 2;
  string county = 3;
}

service Echo {
  rpc Get(User) returns (User) {}
  rpc AddOrUpdate(User) returns (User) {}
  rpc Delete(User) returns (User) {}
}
1.4 gateway

功能:配置并启动 gRPC-Gateway,将 HTTP 请求转换为 gRPC 调用。

1.4.1 gateway.go

功能 :配置并启动 gRPC-Gateway,将 HTTP 请求转发给 gRPC 服务器。
关键点

  • 多路复用器 :使用 runtime.NewServeMux() 创建一个 HTTP 处理多路复用器。
  • 连接选项 :使用 grpc.WithTransportCredentials(insecure.NewCredentials()) 表示使用不安全的连接(仅用于开发环境)。
  • 注册处理程序 :使用 gw.RegisterEchoHandlerFromEndpoint 将 gRPC 方法映射为 HTTP 请求,连接到 gRPC 服务器(默认端口 :50051)。
  • HTTP 服务器 :启动 HTTP 服务器,监听在 :8081 端口。
go 复制代码
func Run() error {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	mux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
	err := gw.RegisterEchoHandlerFromEndpoint(ctx, mux, *grpcServerEndpoint, opts)
	if err != nil {
		return err
	}

	return http.ListenAndServe(":8081", mux)
}

总结

  1. main:应用程序的入口点,负责启动 gRPC 服务器和 gRPC-Gateway,并处理信号以优雅地关闭服务器。
  2. server :实现了 gRPC 服务的具体逻辑,提供了 Get, AddOrUpdate, Delete 方法。
  3. proto:定义了 gRPC 服务的消息格式和服务接口,确保客户端和服务器之间的通信协议一致。
  4. gateway:配置并启动 gRPC-Gateway,将 HTTP 请求转换为 gRPC 调用,提供 RESTful API 接口。

https://github.com/0voice

相关推荐
Scott9999HH3 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社4 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海4 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖5 小时前
JDK 26 新特性详解
java·开发语言
马优晨5 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区7 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大8 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai8 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜8 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa8 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio