1. 前置条件
- Go
- Protocol buffer编译器:
protoc
,安装可参考Protocol buffer编译安装 - Go 协议编译插件
- 下载并安装编译插件,这里确保已配置代理可以下载。
12345$ export GO111MODULE=on # Enable module mode$ go get google.golang.org/protobuf/cmd/protoc-gen-go \google.golang.org/grpc/cmd/protoc-gen-go-grpc#在所在项目中,执行以下命令可指定版本#go get github.com/golang/protobuf/protoc-gen-go@v1.0.0 - 添加路径到环境变量,使得protoc编译其可找到go编译插件
1$ export PATH="$PATH:$(go env GOPATH)/bin"
- 下载并安装编译插件,这里确保已配置代理可以下载。
2. 获取示范代码
代码资源是grpc-go部分代码
1 2 3 4 5 |
```bash #下载代码或直接下载压缩包 git clone -b v1.35.0 https://github.com/grpc/grpc-go cd grpc-go/examples/helloworld ``` |
3. 运行示范代码
- 服务端
进入目录examples/helloworld,编译并执行服务端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
$ go run greeter_server/main.go # server代码 package main import ( "context" "flag" "fmt" "log" "net" "google.golang.org/grpc" pb "google.golang.org/grpc/examples/helloworld/helloworld" ) var ( port = flag.Int("port", 50051, "The server port") ) // server is used to implement helloworld.GreeterServer. type server struct { pb.UnimplementedGreeterServer } // SayHello implements helloworld.GreeterServer func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { log.Printf("Received: %v", in.GetName()) return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil } func main() { flag.Parse() lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port)) if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) log.Printf("server listening at %v", lis.Addr()) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } } |
- 客户端
在另一个终端中,编译并执行客户端代码,查看客户输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
$ go run greeter_client/main.go Greeting: Hello world #Client 代码 package main import ( "context" "flag" "log" "time" "google.golang.org/grpc" pb "google.golang.org/grpc/examples/helloworld/helloworld" ) const ( defaultName = "world" ) var ( addr = flag.String("addr", "localhost:50051", "the address to connect to") name = flag.String("name", defaultName, "Name to greet") ) func main() { flag.Parse() // Set up a connection to the server. conn, err := grpc.Dial(*addr, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := pb.NewGreeterClient(conn) // Contact the server and print out its response. ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %s", r.GetMessage()) } |
- protobuffer接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#helloworld.proto文件 syntax = "proto3"; option go_package = "google.golang.org/grpc/examples/helloworld/helloworld"; option java_multiple_files = true; option java_package = "io.grpc.examples.helloworld"; option java_outer_classname = "HelloWorldProto"; package helloworld; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; } |
4. 升级gRPC服务
5. 重生成gRPC代码
在使用新服务前,需要重编译已经修改的.proto文件;
这里还是在目录examples/helloworld中,运行如下命令:
1 2 3 |
$ protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ helloworld/helloworld.proto |
这样重生产了代码helloworld/helloworld.pb.go和helloworld/helloworld_grpc.pb.go,包含如下:
- 填充、序列化、和检索的HelloRequest和HelloReply 类型;
- 生成客户端和服务端代码.
5.1. 生成protobuf代码
- 不需要生成xxx_grpc.pb.go文件执行如下命令
1 |
$ protoc -I "${GOBGP}"/api --go_out=plugins=grpc:${GOBGP}/api "${GOBGP}"/api/capability.proto "${GOBGP}"/api/attribute.proto |
5.2. 生成包含grpc的protobuf代码
- 如果需要生成grpc.go文件执行如下命令,${GOBGP}为某一项目路径;--go-grpc_out表示XX_rpc需要生成路径;
1 2 3 4 5 6 7 8 9 10 |
# service Greeter { # // Sends a greeting # rpc SayHello (HelloRequest) returns (HelloReply) {} # } #第一种方式(建议): protoc -I "${GOBGP}"/api --go_out=plugins=grpc:${GOBGP}/api "${GOBGP}"/api/*.proto #第二种方式: 需要额外二进制文件protoc-gen-go-grpc,位于项目github.com/golang/protobuf中目录protoc-gen-go $ protoc -I "${GOBGP}"/api --go_out=${GOBGP}/api --go-grpc_out=${GOBGP}/api "${GOBGP}"/api/gobgp.proto |
6. 警告缺少go_package
警告WARNING: Missing 'go_package' option in "gobgp.proto"please specify it with the full Go package path as
a future release of protoc-gen-go will require this be specified。
每个*.proto添加option go_package = "./;gobgpapi";
在syntax下面添加option信息
option go_package = "aaa;bbb";
aaa 表示生成的go文件的存放地址,会自动生成目录的。
bbb 表示生成的go文件所属的包名
7. undefined: grpc.SupportPackageIsVersion6 grpc.ClientConnInterface 问题解决
-
问题原因
由于protoc的 go语言插件protoc-gen-go与google.golang.org/grpc版本不兼容所致,
因为grpc(google.golang.org/grpc)降了到了v1.36.1,高版本protoc-gen-go编译出来的your-module.pb.go不兼容低版本的grpc,所以protoc-gen-go也要相应降级。这里安装版本如下,123github.com/golang/protobuf v1.0.0protoc-gen-go v1.23.0;protoc 建议版本 v3.7.1 ,否则编译protobuf不支持格式; -
方法一:下载指定版本库的具体步骤
- 先删除当前项目中的vendor目录:
rm -rf vendor
或手动删除; - 再替换go.mod中依赖库版本:
go mod edit -require="github.com/golang/protobuf@v1.0.0"
go mod edit -require="google.golang.org/grpc@v1.5.1"
- 下载指定版本v1.26.0:
go get -u -x google.golang.org/grpc@v1.5.1
go get -u -x github.com/golang/protobuf@v1.0.0
- 然后再
go mod vendor
- 再运行程:
go build main.go
- 先删除当前项目中的vendor目录:
-
方法二
参考该片文章undefined: proto.ProtoPackageIsVersion3
赞赏
微信赞赏
支付宝赞赏