go 进阶 RPC相关: 三. grpc-gateway

四. grpc-gateway 支持

  1. 什么是grpc-gateway: 在上方我们编写了一个grpc服务端,并且编写了grpc客户端用来访问服务端指定接口,那么这个grpc服务端中的接口如何才能被外部通过http,RESTful 请求访问,支持基于json传递数据
  2. 也可以简单理解为实现一个grpc网关代理服务,可以通过http,RESTful风格访问这个代理服务,由这个代理服务通过grpc方式访问实际的grpc服务,最终实现grpc服务可以支持http,RESTful,json
  3. 实现方式:
  1. 运行grpc服务,并拿到访问地址,作为最终的目标服务(当前就是上方编写的grpc服务端)
  2. 下载安装grpc-gateway需要相关插件
  3. 因为是让目标服务支持http,拿到目标服务的.proto文件,改写使其支持http, 支持RESTful, 支持json
  4. 执行编译命令,通过安装的grpc-gateway插件,编译改写的.ptoto文件,生成支持http的相关go文件,也就是一个以".pb.gw.go"结尾的文件
  5. 基于新生成的".pb.gw.go"文件,创建grpc网关代理服务,设置代理服务访问地址,代理服务持有访问目标服务的地址
  6. 使用http RESTful访问代理服务,传递json数据,由代理服务将http请求转换为protobuf格式作为grpc数据发送到目标服务
  1. 简单一句话就是grpc-gateway是Protocol Buffers编译器协议的一个插件,通过这个插件读取Protobuf服务定义并生成一个反向代理服务器,该服务器将RESTful HTTP API转换为grpc,请求实际的grpc服务,这个代理服务底层时通过google.api.http annotations注解生成的
  2. 请求流程图
    在这里插入图片描述

1. grpc服务

  1. 提供一个grpc服务端作为目标服务, 该服务是由grpc通信,现在的需求是让这个服务中的某个接口支持http RESTful访问
  2. 当前使用上方"服务端代码示例"中写的grpc服务作为目标服务,现在需要做的是
  1. 明确需求: 让该服务中的MethodOne()接口支持http RESTful访问,
  2. 拿到该服务的访问地址,作为目标服务,访问地址是: “localhost:8080”
  3. 获取到生成该服务端".proto"文件,后面要改写这个文件,通过改写后的文件生成创建网关代理服务需要的以".pb.gw.go"结尾的文件

2. 安装grpc-gateway相关插件

  1. 参考博客
  1. 参考博客1
  2. 参考博客2
  3. 参考博客3
  4. 参考博客4
  1. 下载protoc-gen-grpc-gateway包
go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
  1. 安装
go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
  1. 此时查看"/GOPATH/bin/"目录下加上前面grpc相关文件一共有四个相关的.exe可执行文件
    在这里插入图片描述

3. 改写.proto文件

  1. 在第一步已经拿到了grpc目标服务的.proto文件,并且知道目标服务中的MethodOne()接口要支持http访问
  2. 改写.proto文件,设置指定接口支持http
  1. 通过import导入"google/api/annotations.proto"原因是grpc-gateway是基于该注解实现的
  2. 目标服务的MethodOne()接口要支持http,修改.proto中的这个接口,设置支持http,设置接口请求方式,请求路径,与传参格式
  3. 发送http, post请求访问代理服务的"/v1/sayHello"时,代理服务会将请求代理到grpc目标服务的MethodOne()接口
  4. 其它不变
//todo 指定生成proto时用的语法版本
syntax = "proto3";
//当前文件所在包
package test;
//todo 指定生成的go文件所在目录包,"."表示在当前目录生成,"proto"表示生成的go文件的包名是proto
option go_package = ".;test";

//必须要导入(grpc-gateway是基于该注解实现的,报红不用管)
import "google/api/annotations.proto";

//todo 定义请求参数类型
message TestRequest {
  //参数类型 变量名=标号
  string message = 1;
}
//todo 定义响应参数类型
message TestResponse {
  string message = 1;
  int32 code = 2;
}

//todo 定义server
service MyGrpc {
  //todo 定义接口

  //内部增加grpc-gateway支持
  rpc MethodOne(TestRequest) returns (TestResponse) {
    //1.google.api.http表示支持http
    option (google.api.http) = {
      //2.指定请求方法为post或get等等
      //"/v1/sayHello": 指定的接口访问路径
      post: "/v1/sayHello"
      //3.设置请求数据格式"*"表示json结构体?
      body: "*"
    };
  }
  //定义以流式响应的接口
  rpc ServerStreaming(TestRequest) returns (stream TestResponse) {}
  //定义以流式接收数据的接口
  rpc ClientStreaming(stream TestRequest) returns (TestResponse) {}
  //定义双向数据流接口
  rpc BidirectionalStreaming(stream TestRequest) returns (stream TestResponse) {}
}

4. 执行protoc命令生成用来创建代理服务的文件

  1. 回到安装grpc-gateway相关插件位置,在上方我们首先执行了一个go get命令,下载了一个工程到本地
go get -u github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
  1. 当该命令执行完毕后,会下载一个包到本地的"$GOPATH\pkg\mod\github.com"下,在goland编辑工具当前项目的External Libraries位置也可以看到,我们编译.proto生成支持grpc-gateway功能的文件,就是通过该包下的几个文件实现的
    在这里插入图片描述
  2. 编写的.proto文件中import导入的包与接口支持http用到的就在"github.com\grpc-ecosystem\grpc-gateway@v1.16.0\third_party\googleapis\google"这个文件夹中
  3. 正常情况下切换到.proto所在文件执行下方命令编译生成文件即可(我这里一直找报找不到需要的导入到文件)

proto编译引用外部包问题

//-I:表示引用外部包所在路径,
//当前表示编译my_test.proto这个文件,该文件需要导入third_party/googleapis中的文件
protoc -I $GOPATH/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.16.0/third_party/googleapis --go_out=plugins=grpc:. ./my_test.proto
  1. 由于上方一直报错,解决这个问题,创建一个文件夹例如"pbfiles"将"$GOPATH/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.16.0/third_party/googleapis/google"这个文件夹复制到"pbfiles"中,或者在goland编辑工具当前项目的External Libraries位置—> github.com\grpc-ecosystem\grpc-gateway@v1.16.0—>third_party—>googleapis—>google,复制google文件夹到自己创建的"pbfiles"文件夹中
    在这里插入图片描述
  2. 将需要编译的.proto文件也放到自定义创建的"pbfiles"文件夹,最终效果(注意需要编译的"my_test.proto"文件与goole文件夹是平级的)
    在这里插入图片描述
  3. 切换到自定义创建的"pbfiles"文件夹执行编译命令开始编译
//这是两个命令可以分解为
//命令1: protoc --go_out=. my_test.proto 
//命令2: protoc --go-grpc_out=. my_test.proto 
//其中"=."表示编译生成的文件到当前目录,也可以将"."替换为"/目录名"将编译后的文件生成到指定目录(注意该目录必须存在)
protoc --go_out=. --go-grpc_out=. my_test.proto 

//生成gateway相关命令,其中":."表示编译生成文件到当前目录,执行完毕后会生成一个"文件名.pb.gw.go"文件
//也可"protoc --grpc-gateway_out=logtostderr=true:../service my_test.proto" 通过"../目录"指定,但是该目录必须存在
protoc --grpc-gateway_out=logtostderr=true:. my_test.proto
  1. 编译完成后会生成3个文件
    在这里插入图片描述
  2. 通过生成的"文件名.pb.gw.go"文件构建支持http的grpc-gateway服务

5. 解释用来创建grpc-gateway网关代理服务的".pb.gw.go"文件

  1. 查看生成的用来构建 grpc-gateway服务的"文件名.pb.gw.go"文件,内部有一个"RegisterXXXxxHandlerFromEndpoint()"函数(XXXxx通常时目标grpc服务的服务名),在创建代理服务时通过该方法,让代理服务与目标服务绑定:
  1. mux *runtime.ServeMux: 代理服务自己的路由
  2. endpoint string: 目标服务地址
  3. 通过该方法将目标服务注册到代理服务路由中
// RegisterMyGrpcHandlerFromEndpoint is same as RegisterMyGrpcHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterMyGrpcHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
	conn, err := grpc.Dial(endpoint, opts...)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			if cerr := conn.Close(); cerr != nil {
				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
			}
			return
		}
		go func() {
			<-ctx.Done()
			if cerr := conn.Close(); cerr != nil {
				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
			}
		}()
	}()

	return RegisterMyGrpcHandler(ctx, mux, conn)
}
  1. 其它的还没看

6. 编写grpc-gateway网关代理服务

  1. 用来创建grpc-gateway网关代理服务的以".pb.gw.go"结尾的文件已经生成,目标服务也已经启动运行,并且拿到了目标服务的访问地址"localhost:50055",接下来开始编写代理服务,代理服务持有目标服务,通过http RESTful访问代理服务,代理服务接收到请求后,将http请求转换为protobuf格式作为grpc数据发送到目标服务
package main

import (
	"context" // Use "golang.org/x/net/context" for Golang version <= 1.6
	"github.com/golang/glog"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"google.golang.org/grpc"
	"net/http"
)

func run() error {
	//1.创建ctx
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)

	//退出时取消
	defer cancel()

	//2.创建一个代理服务的路由
	//可以理解为每个rs都需要持续跟下游建立连接
	mux := runtime.NewServeMux()
	opts := []grpc.DialOption{grpc.WithInsecure()}

	//3.目标服务地址
	grpcServerAddr := "localhost:8080"

	//4.调用".pb.gw.go"文件中生成的RegisterMyGrpcHandlerFromEndpoint()函数
	//将目标服务注册到代理服务的路由中
	err := RegisterMyGrpcHandlerFromEndpoint(ctx, mux, grpcServerAddr, opts)
	if err != nil {
		return err
	}

	//5.监听端口启动服务,当前代理服务端口为8081,并放入当前代理服务的路由
	return http.ListenAndServe(":8081", mux)
}

func main() {
	defer glog.Flush()
	if err := run(); err != nil {
		glog.Fatal(err)
	}
}
  1. 此时代理服务的地址是"localhost:8081",持有的目标服务地址是"localhost:8080",发送http post请求访问代理服务的"localhost:8081/v1/sayHello"接口时,代理服务会将请求代理到grpc服务的MethodOne()接口
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`io.grpc:protoc-gen-grpc-java:1.0.0:exe:${os.detected.classifier}` 是一个 Maven 坐标,用于使用 gRPC 的 Protocol Buffers 编译器插件来生成 gRPC 相关的 Java 代码。 这个坐标指定了以下部分: - `io.grpc` 是 Maven 组织 ID,表示该插件是由 gRPC 提供的。 - `protoc-gen-grpc-java` 是插件的名称,用于生成 gRPC 相关的 Java 代码。 - `1.0.0` 是插件的版本号,表示要使用的插件版本。 - `exe:${os.detected.classifier}` 指定了插件的文件类型和操作系统相关的后缀。 `${os.detected.classifier}` 是一个 Maven 变量,用于根据操作系统自动选择相应的插件文件。它会根据当前操作系统选择适当的文件后缀,例如在 Windows 上是 `.exe`,在 Linux 上是 `.linux-x86_64`。 通过在 Maven 项目的 `pom.xml` 文件中添加该依赖项,您可以在构建过程中自动下载并使用该插件来生成 gRPC 的 Java 代码。例如: ```xml <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.0.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> ``` 这样配置后,您可以使用 `mvn compile` 命令来自动生成 gRPC 的 Java 代码。生成的代码将位于 `target/generated-sources/protobuf` 目录下。 请确保您的 Maven 项目中已经包含了正确的依赖项,并且配置文件中的版本号与您所需的版本一致。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值