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

服务端编写完毕之后直接启动,发现没有问题,接下来我们编写客户端。

package main import ( "context" "fmt" "google.golang.org/grpc" "matsuri/yoyoyo" ) func main() { conn, err := grpc.Dial("127.0.0.1:33333", grpc.WithInsecure()) if err != nil { fmt.Println(err) return } defer func() { _ = conn.Close() }() client := yoyoyo.NewMatsuriClient(conn) response, _ := client.HelloMatsuri( context.Background(), &yoyoyo.MatsuriRequest{Name: "夏色祭", Age: 16}, ) fmt.Println(response.Result) // name: 夏色祭, age 16 }

我们看到整体没有任何问题,还是很简单的。

Python 和 Go 相互调用

下面我们来看看如何实现 Python 和 Go 之间的互相调用,Python 编写服务端,Go 去访问;Go 编写服务端,Python 去访问。

注意:如果实现互相调用,那么它们 proto 文件中的类、方法等信息要完全一致。

下面来编写 proto 文件,为了方便这里的 proto 文件名我们就不改了,还叫原来的 matsuri.proto:

syntax = "proto3"; // 如果是 Go 的话,那么只需要加上 option go_package = ".;包名"; option go_package = ".;包名"; service Mea { rpc HelloMea(request) returns (response){} } message request { string name = 1; int32 age = 2; } message response { string result = 1; }

编写 Go 的服务端:

package main import ( "context" "fmt" "google.golang.org/grpc" "matsuri/yoyoyo" "net" ) type Mea struct { } func (m *Mea) HelloMea (ctx context.Context, request *yoyoyo.Request) (*yoyoyo.Response, error) { // 参数 request 是小写, 这里自动帮我们变成了大写,同理还有下面的 response name := request.Name age := request.Age return &yoyoyo.Response{Result: fmt.Sprintf("你好: %s, %d 岁的单亲妈妈", name, age)}, nil } func main() { // 创建服务端 server := grpc.NewServer() yoyoyo.RegisterMeaServer(server, &Mea{}) listener, err := net.Listen("tcp", ":33333") if err != nil { fmt.Println(err) return } if err = server.Serve(listener); err != nil { fmt.Println(err) return } }

编写 Python 的服务端:

import grpc import matsuri_pb2_grpc as pb2_grpc import matsuri_pb2 as pb2 class Mea(pb2_grpc.MeaServicer): def HelloMea(self, request, context): name = request.name age = request.age return pb2.response(result=f"name is {name}, {age} years old") if __name__ == \'__main__\': from concurrent.futures import ThreadPoolExecutor grpc_server = grpc.server(ThreadPoolExecutor(max_workers=4)) pb2_grpc.add_MeaServicer_to_server(Mea(), grpc_server) grpc_server.add_insecure_port("127.0.0.1:22222") grpc_server.start() grpc_server.wait_for_termination()

两个语言的服务端,我们就编写完毕了,Python 服务端监听 22222 端口,Go 服务端监听 33333 端口,那么下面来编写客户端。Python 客户端调用 Go 服务端,Go 客户端调用 Python 服务端。

package main import ( "context" "fmt" "google.golang.org/grpc" "matsuri/yoyoyo" ) func main() { // 连接 22222 端口,这是 Python 服务 conn, err := grpc.Dial("127.0.0.1:22222", grpc.WithInsecure()) if err != nil { fmt.Println(err) return } defer func() { _ = conn.Close() }() client := yoyoyo.NewMeaClient(conn) response, _ := client.HelloMea( context.Background(), &yoyoyo.Request{Name: "神乐 mea", Age: 38}, ) fmt.Println(response.Result) // name is 神乐 mea, 38 years old }

我们看到 Go 调用 Python 的服务是完全没有问题的,那么 Python 调用 Go 呢?

import grpc import matsuri_pb2_grpc as pb2_grpc import matsuri_pb2 as pb2 channel = grpc.insecure_channel("127.0.0.1:33333") client = pb2_grpc.MeaStub(channel=channel) response = client.HelloMea( pb2.request(name="神乐 mea", age=38) ) print(response.result) # 你好: 神乐 mea, 38 岁的单亲妈妈

此时我们就通过 gRPC 和 protobuf 完成了 Python 和 Go 之间的 rpc 调用。

gRPC 的流模式

上面我们介绍了 gRPC 的使用方式,下面介绍 gRPC 中的 stream,也就是流模式。流模式可以源源不断地推送数据,因此很适合传输一些大数据,或者服务端和客户端之间进行长时间的数据交互。比如客户端可以向服务端订阅一个数据,服务端就可以利用 stream 源源不断地向客户端推送数据。

而流模式总共有以下几种:

服务端数据流模式(Server-side streaming RPC)

客户端数据流模式(Client-side streaming RPC)

双向数据流模式(Bidirectional-side streaming RPC)

而我们上面一直演示的例子,使用的都是简单模式(Simple RPC),下面来介绍剩余的三种流模式。

服务端数据流:

这种模式是客户端发起一次请求,服务端返回一段连续的数据流。典型的例子是客户端向服务端发送一个股票代码,服务端就把该股票的实时数据源源不断地返回给客户端。

客户端数据流:

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

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