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

注意:如果你发现报错了,出现如下异常:module \'google.protobuf.descriptor\' has no attribute \'_internal_create_key\',说明是你的 protobuf 版本过低导致的,可以通过升级 protobuf 来解决。

pip install --upgrade protobuf -i https://pypi.tuna.tsinghua.edu.cn/simple

服务端编写完毕,下面我们来编写客户端。

# 客户端 import grpc import matsuri_pb2 as pb2 import matsuri_pb2_grpc as pb2_grpc # 定义一个频道, 连接至服务端监听的端口 channel = grpc.insecure_channel("127.0.0.1:22222") # 生成客户端存根 client = pb2_grpc.MatsuriStub(channel=channel) # 然后我们就可以直接调用 Matsuri 服务里面的函数了 print("准备使用服务了~~~~") while True: name, age = input("请输入姓名和年龄, 并使用逗号分割:").split(",") # 调用函数, 传入参数 matsuri_request, name 和 age 位于 matsuri_request 中; 因为不能直接发送, 需要序列化成 protobuf # 注意: 必须是 matsuri_request, 因为我们在 protobuf 文件定义的就是 matsuri_request matsuri_response = client.hello_matsuri( pb2.matsuri_request(name=name, age=int(age)) ) # result 位于返回值 matsuri_response 中, 直接通过属性访问的形式获取 # 而之所以能够这么做, 也是客户端存根在背后为我们完成的, 当然这里也可以不叫 matsuri_response, 它只是一个变量名 print(matsuri_response.result)

下面我们来执行客户端,调用一下试试。

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

所以整体逻辑还是比较简单的,当然这是因为背后有很多细节都自动帮我们完成了,而核心就是那两个自动生成的文件,我们只需要关注业务逻辑即可。但是注意:自动生成的两个文件,我们不要擅自改动它,除非你对 protobuf 协议非常的了解。

然后问题来了,我们来看看采用 protobuf 协议序列化之后的结果是什么,不是说它比较高效吗?那么我们怎能不看看它序列化之后的结果呢,以及它和 json 又有什么不一样呢?

import matsuri_pb2 as pb2 request = pb2.matsuri_request(name="koishi", age=15) # 调用 SerializeToString 方法会得到一个二进制的字符串 print(request.SerializeToString()) # b\'\n\x07matsuri\x10\x10\' # 这个字符串显然我们看不懂,我们暂时也不去深究它的意义,总之这就是 protobuf 序列化之后的结果 # 而且我们还可以将其反序列化,不然服务端接收到之后也不认识啊 request2 = pb2.matsuri_request() request2.ParseFromString(b\'\n\x06koishi\x10\x0f\') print(request2.name) # koishi print(request2.age) # 15 """ 是可以正常反序列化的,所以我们不认识没关系,protobuf 认识就行 那么 b\'\n\x06koishi\x10\x0f\' 到底是啥意思呢? 首先里面的 \x06 表示后面的 6 个字符代表 name 参数的值,而之所以是 name 不是 age 是因为我们在定义 protobuf 文件的时候,name 参数的位置是第 1 个 而 \x0f 就是 16 进制的 15 """ # 然后来看看 json import simplejson as json print(json.dumps({"name": "koishi", "age": 15}).encode("utf-8")) # b\'{"name": "koishi", "age": 15}\' # 可以看到 protobuf 协议序列化之后的结果要比 json 短, 平均能得到一倍的压缩

以上便是 Python 实现简单的 gRPC 服务,可以看到至少在业务层面还是比较简单的。

grpc 文件的 import 问题

我们看一下自动生成的 grpc 文件,里面存在一个问题。

import matsuri_pb2 as matsuri__pb2

在 matsuri_pb2_grpc.py 中导入了 matsuri_pb2.py,因为 grpc 需要使用 protobuf,但是这样导包是不是存在一些问题呢?首先这两个文件必须要在同一个目录,这是毋庸置疑的,否则会导入失败。但是这行代码还隐含了我们的客户端和服务端代码也要和这两个文件在同一个目录,但如果不是这样会有什么后果呢?我们画一张图:

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

假设我们的工程目录叫 dir1,我们的客户端、服务端代码位于该目录中,但是自动生成的两个 py 文件我们将其放到一个单独的目录 dir2 中。这个时候如果客户端或服务端导入 matsuri_pb2_grpc.py 的时候会发生什么问题?显然会报错,因为客户端或服务端在导入的时候,工作区是 dir1 目录,然后 matsuri_pb2_grpc 中再 import matsuri_pb2 显然就找不到了,因为它位于 dir2 目录中。

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

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