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.protohttp.protohttpbody.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网关代码的主要内容,如果未能解决你的问题,请参考以下文章

protoc生成gRPC代码和HTTP网关代码

grpc-gateway:grpc网关

grpc proto文件生成java.net实体类以及客户端代码

从源代码安装 grpc 时出现“make: protoc: Command not found”

grpc-环境与示例

gRPC最佳入门教程,Golang/Python/PHP多语言讲解