protoc生成gRPC代码和HTTP网关代码
Posted 玩家_名狱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了protoc生成gRPC代码和HTTP网关代码相关的知识,希望对你有一定的参考价值。
本文大多参考:https://www.jb51.net/article/251828.htm
为什么需要网关层,因为grpc调用是用于程序内部的远程调用协议,但是我们测试接口的时候通常使用http协议,http协议是应用层的协议,和grpc不相通,因此我们希望有一层可以帮我们转换,把我们调用的http协议数据转下格式,然后发给grpc服务接口,这就是这个网关做的事情。
1、确保protobuf(protoc)已经安装好了
如果能使用命令得到版本信息说明是成功的
xxx@xxx:~$ protoc --version
libprotoc 3.21.7
golang的环境变量也配置好了
xxx@xxx:~$ vim ~/.bashrc
export GOROOT=/usr/local/go/
export GOPATH=/home/xxx/go
export PATH=$PATH:$GOROOT
2、安装插件
xxx@xxx:~$ go install google.golang.org/protobuf/cmd/protoc-gen-go@laster
xxx@xxx:~$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@laster
xxx@xxx:~$ go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
xxx@xxx:~$ go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
xxx@xxx:~$ go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
xxx@xxx:~$ go install github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
如果你曾经使用过旧版本的protoc-gen-go,现在默认安装最新版的,使用上有点坑,可看这个例子:https://blog.stdioa.com/2020/12/protobuf-upgrade/
把插件目录配置到PATH变量中
xxx@xxx:~$ vim ~/.bashrc
# 添加内容如下
export PATH=$PATH:/home/xxx/go/bin
# 保存退出后更新一下环境配置
xxx@xxx:~$ source ~/.bashrc
3、项目配置
# 创建项目目录
xxx@xxx:~$ mkdir -p ~/go/src/mycode
xxx@xxx:~$ cd ~/go/src/mycode/
xxx@xxx:~/go/src/mycode$ go mod init mycode
开始创建proto文件
xxx@xxx:~/go/src/mycode$ mkdir proto
xxx@xxx:~/go/src/mycode$ cd proto/
# 编写一个proto文件
xxx@xxx:~/go/src/mycode/proto$ vim apple.proto
apple.proto内容如下
syntax = "proto3";
package apple;
option go_package ="./apple";
import "google/api/annotations.proto";
service Apple
rpc GetList(RequestMessage) returns (ResponseMessage)
option (google.api.http) =
post: "/v1/apples/get-list"
body: "*"
;
message RequestMessage
int32 page_index = 1;
int32 page_size = 2;
message ResponseMessage
string data = 1;
proto文件里面使用到了 import "google/api/annotations.proto";
需要从其他包里面拷贝过来
xxx@xxx:~/go/src/mycode/proto$ mkdir common
# 这个annotations.proto文件所在路径和你的可能有一点差异,因为版本不同
xxx@xxx:~/go/src/mycode/proto$ mv ~/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.16.0/third_party/googleapis/google/api/* ./common
拷贝的proto文件有三个,
annotations.proto
,http.proto
,httpbody.proto
,httpbody.proto用不到,可以删除。
4、开始生成
# 回到项目根路径
xxx@xxx:~/go/src/mycode/proto$ cd ../
# 创建一个文件夹存放生成结果
xxx@xxx:~/go/src/mycode$ mkdir genproto
# 开始生成
xxx@xxx:~/go/src/mycode$ protoc -I ./proto/common -I ./proto --go-grpc_out=./genproto --go_out=./genproto --grpc-gateway_out=logtostderr=true:./genproto --swagger_out=logtostderr=true:./genproto ./proto/apple.proto
-I 参数指定查找proto文件的目录。protoc不知道你import的其它proto文件在哪的。
–go-grpc_out 参数指定grpc Service代码存放的位置
–go_out参数指定grpc Message参数的存放位置。protoc-gen-go插件新版生成的Message和Service不在一个文件内
–grpc-gateway_out 参数指定参数和grpc网关代码存放的位置(logtostderr=true是一个参数如果还有其他参数则使用逗号分隔,例如--grpc-gateway_out=logtostderr=true,xxx=xxx:./
,冒号后面是路径)
*–swagger_out 参数指定接口文档的说明配置
最后添加要使用生成的proto文件 ./proto/apple.proto
最终有四个文件生成genproto/apple/apple_grpc.pb.go
存放生成service的代码、genproto/apple/apple.pb.go
存放生成message的代码、genproto/apple/apple.pb.gw.go
存放网关的代码、genproto/apple.swagger.json
存放接口文档描述配置
5、编写网关代码
xxx@xxx:~/go/src/mycode$ mkdir grpc_gateway
xxx@xxx:~/go/src/mycode$ vim grpc_gateway/grpc_gateway.go
package grpc_gateway
import (
"flag"
"net/http"
"log"
"github.com/golang/glog"
"golang.org/x/net/context"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"google.golang.org/grpc"
gw "mycode/genproto/apple"
)
var (
appleEndpoint = flag.String("apple_endpoint", "localhost:6666", "endpoint of Gateway")
)
func run() error
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
mux := runtime.NewServeMux()
opts := []grpc.DialOptiongrpc.WithInsecure()
err := gw.RegisterAppleHandlerFromEndpoint(ctx, mux, *appleEndpoint, opts)
if err != nil
return err
log.Println("网关服务开启")
return http.ListenAndServe(":7777", mux)
func GrpcGateway()
flag.Parse()
defer glog.Flush()
if err := run(); err != nil
glog.Fatal(err)
6、编写gRPC服务器代码
package main
import (
"log"
"net"
pb "mycode/genproto/apple"
gg "mycode/grpc_gateway"
"google.golang.org/grpc"
"golang.org/x/net/context"
)
const (
PORT = ":6666"
)
type Server struct
// 新版生成的代码里面必须要继承这个类
*pb.UnimplementedAppleServer
func (s *Server) GetList(ctx context.Context, in *pb.RequestMessage) (*pb.ResponseMessage, error)
log.Println("request: ", in.PageSize, in.PageIndex)
return &pb.ResponseMessageData:"还有很多苹果哦!!!", nil
func main()
lis, err := net.Listen("tcp", PORT)
if err != nil
log.Fatalf("failed to listen: %v", err)
s := grpc.NewServer()
pb.RegisterAppleServer(s, &Server)
log.Println("rpc服务已经开启")
// 这里启动网关
go gg.GrpcGateway()
s.Serve(lis)
7、测试调用
http接口调用
curl --location --request POST 'http://127.0.0.1:7777/v1/apples/get-list' \\
--header 'Content-Type: application/json' \\
--data-raw '
"page_index":1,
"page_size":5
'
“data”: “还有很多苹果哦!!!”
8、踩过的坑
1、新版protoc-gen-go生成的代码里面要求我们注册的服务对象里面必须实现mustEmbedUnimplementedAppleServer
方法,按道理我们可以在结构体中绑定这个方法,如func (s *Server)mustEmbedUnimplementedAppleServer(),也可以继承实现了这个方法的对象,如 pb.UnimplementedAppleServer 对象。对于我们写代码的人来说最好是自己实现绑定这个方法,但是你看他这个方法名称 “必须嵌入未实现的apple服务器”,然后你看这个对象的名称 “未实现的apple服务器”,明摆着让我们必须继承。
更可恶的是,你自己实现这个方法时,他报错了,你必须继承这个对象。。。
2、在旧版中,生成不包含网关代码时只需要用到 --go_out 参数即可,现在区分开了,这个参数只生成message相关的代码。使用 --go-grpc_out 参数才生成与service相关的代码。
3、写网关的代码里面注意网关的端口和服务的端口不能相同,而且在main函数里面启动网关时主要要使用协程的方式启动。
以上是关于protoc生成gRPC代码和HTTP网关代码的主要内容,如果未能解决你的问题,请参考以下文章
grpc proto文件生成java.net实体类以及客户端代码