protobuf 介绍,Python 和 Go 编写 rpc 服务(gRPC) (9)

所以如果我们要将其放在一个单独的目录中(假设叫 grpc_helper),那么我们应该将 matsuri_pb2_grpc 中的导入逻辑改成这样子:

from . import matsuri_pb2 as matsuri__pb2

然后客户端和服务端使用下面的方式导入:

from grpc_helper import matsuri_pb2 as pb2 from grpc_helper import matsuri_pb2_grpc as pb2_grpc

这样做就没有问题了,所以要注意相应的导包逻辑。但是这并不能算是一个 bug,因为 Python 的导包逻辑就是如此,而在生成文件的时候它也显然不可能猜测出你要把文件放在哪个目录中,所以这种情况我们需要手动设置。

go 下的 gRPC 初体验

看完了 Python 的 gRPC,我们再来看看 Go 的 gRPC,既然了解了 Python 的 gRPC,那么再看 Go 的 gRPC 会简单很多。

首先要安装 protoc,它是 protobuf 的编译工具,它适用于所有的语言,我们去 https://github.com/protocolbuffers/protobuf/releases 下载对应操作系统的 protoc 即可,这里我下载的是 protoc-3.14.0-win64.zip。解压之后,将里面的 bin 目录添加到环境变量中。

我们生成 Python 对应的文件时,是通过 grpc_tools 这个模块来帮我们生成的,它里面实现了 protoc 的功能。但是对于 Go 而言,我们需要使用 protoc 这个编译工具,当然这个工具也适用于 Python。

然后安装 Go 的第三方包:

go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct go get -u github.com/golang/protobuf/protoc-gen-go go get -u google.golang.org/grpc

安装之后,我们来编写 protobuf 文件,我们说 protobuf 是支持多语言的,所以只需要做一些简单的修改即可。

syntax = "proto3"; // 这里需要指定我们生成的是 go 的 package,其它语言也是同理 // 然后里面的 ".;yoyoyo" 是什么鬼? 首先 go 的源文件一定要有一个 package,而 .; 后面的 yoyoyo 便是指定对应的包名 option go_package = ".;yoyoyo"; // 注意:由于 Go 的可导出性,我们需要将名称改成大写,而且最好遵循驼峰命名法 service Matsuri { rpc HelloMatsuri(MatsuriRequest) returns (MatsuriResponse){} } message MatsuriRequest { string name = 1; // = 1表示第1个参数 int32 age = 2; } message MatsuriResponse { string result = 1; }

然后生成 Go 的源文件,命令如下:

protoc --go_out=plugins=grpc:. -I . matsuri.proto

相信这些命令不需要解释了,都是写死的,执行完之后会发现自动帮你生成了一个 matsuri.pb.go 源文件。注意:只有一个文件,不光是 Go,其它语言都只生成一个文件,只有 Python 是两个文件。

所以在 Go 里面不存在类似于 Python 中的导包问题,因为它只有一个文件。

由于我们生成的 Go 文件中的 package 是 yoyoyo,那么我们最好新建一个目录 yoyoyo,然后将其放在里面。然后我们来编写服务端和客户端代码,首先是服务端。

package main import ( "context" "fmt" "google.golang.org/grpc" "matsuri/yoyoyo" "net" ) type Matsuri struct { } // 接收两个参数,和 Python 类似,Python 的 hello_matsuri 函数的第一个参数是 matsuri_request,第二个参数是 context // matsuri_request 就是实际数据的载体,它是 matsuri_pb2.matsuri_request(),而 context 我们没有用上,后面会介绍 // 在 Go 中也是如此,只不过这两个参数是相反的。 // 第一个参数是 context(这里叫 ctx),它是 context.Context 类型;第二个参数是 matsuri_request,类型是 *yoyoyo.MatsuriRequest(通过包名去调用) // 然后返回值为 *yoyoyo.MatsuriResponse 和 error func (m *Matsuri) HelloMatsuri (ctx context.Context, matsuri_request *yoyoyo.MatsuriRequest) (*yoyoyo.MatsuriResponse, error) { // 我们看到在 proto 文件中是小写的 name 和 age,但是在生成文件的时候自动帮我们变成了 Name 和 Age,所以 protoc 在生成文件的时候会自动帮我们处理不同语言的逻辑 name := matsuri_request.Name age := matsuri_request.Age return &yoyoyo.MatsuriResponse{Result: fmt.Sprintf("name: %s, age %d", name, age)}, nil } func main() { // 创建服务端 server := grpc.NewServer() // 注册, 第一个参数是服务端, 第二个参数是必须实现 HelloMatsuri(context.Context, *MatsuriRequest) (*MatsuriResponse, error) 方法的接口 yoyoyo.RegisterMatsuriServer(server, &Matsuri{}) // 监听端口 listener, err := net.Listen("tcp", ":33333") if err != nil { fmt.Println(err) return } // 开启服务,每来一个连接,内部会开一个协程去处理 if err = server.Serve(listener); err != nil { fmt.Println(err) return } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwfsgj.html