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.DialOption{grpc.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.ResponseMessage{Data:"还有很多苹果哦!!!"}, 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函数里面启动网关时主要要使用协程的方式启动。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值