0%

python调用gRPC接口

背景

​ 最近几个项目都是用的go语言编写的,使用的是 gRPC接口,是没有对外暴露http接口。但在部分场景下,由于可测性的需要必须直接调用 gRPC接口,达到数据的构造,所以需要通过 Python 代码完成调用服务器上的 gRPC接口 。

问题记录

​ 由于网上相关最简单的demo已经是烂了一地。。。我这边就不再去重复写一段demo了(可参考当时我看的一篇Blog,或自行搜索相关前置准备)。

​ 主要记录在实际过程中运用遇到的一些问题,希望能给大家一些帮助。

步骤一:在执行转化脚本命令时候报错,具体信息如下:

1
2
3
4
5
6
7
8
9
10
11
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./user.proto

github.com/gogo/protobuf/gogoproto/gogo.proto: File not found.
base.proto: File not found.
user.proto:7:1: Import "github.com/gogo/protobuf/gogoproto/gogo.proto" was not found or had errors.
user.proto:8:1: Import "base.proto" was not found or had errors.
user.proto:47:5: "NullInt64" is not defined.
user.proto:59:5: "NullInt64" is not defined.
user.proto:86:5: "NullInt64" is not defined.
user.proto:239:5: "NullInt64" is not defined.
user.proto:258:5: "NullInt64" is not defined.

针对报错问题一个个分析。

问题1: github.com/gogo/protobuf/gogoproto/gogo.proto: File not found。
1
2
// 由于转化的脚本中直接以如下方法引用的 gogo.proto 文件,但在转换的时候肯定是找不到的
import "github.com/gogo/protobuf/gogoproto/gogo.proto"

解决办法:

  1. gogo.proto 文件下载到本地,放到与需要转换的 user.proto 文件同级目录下;

  2. 修改 user.proto 的代码。

    1
    2
    # 将如下代码修改成 import "gogo.proto"
    import "github.com/gogo/protobuf/gogoproto/gogo.proto"
问题2: base.proto: File not found。

解决方式同问题1,实际就将依赖的这个 base.proto 文件放到与需要转换的 user.proto 文件同级目录下即可。(由于我的 base.proto 文件中也同问题1中一样引用了 gogo.proto,所以都需要重复问题1的步骤2

经过上面操作,终于将 user.proto 文件转成了对应的python脚本,具体样式如下。

python_convert_grpc.png

但你以为这就完事了??(╯▔︹▔)╯ , Too young too simple

步骤二:调用转换成python脚本的接口。

注:由于无需本地启动一个服务端,所以可以简单点只写一个调用端即可(具体方式类似 BloomRPC,通过 kubectl 代理到远端的 grpc服务端, 具体原理可自行搜索)

在写客户端代码之前,就还存在一个隐藏问题。

问题3: 实际上如果在写客户端代码时候需要引用的其它的proto文件中的方法,则需要其它引用的文件也转成 python脚本(根据个人需要进行转换)。

客户端代码大致如下(我编写的demo中则无需转换其它文件):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import grpc
import user_pb2_grpc, user_pb2

class userService:

@classmethod
def get_mobile_by_id(cls):
# 连接上本地的gRPC代理服务端, 端口为kubectl本地代理的端口
channel = grpc.insecure_channel('localhost:6666')
# 根据对应服务初始化渠道
test_stub = user_pb2_grpc.UserStub(channel)
user_id = 1234
# 根据具体的 Request 定义来传入参数, 否则会报错
return test_stub.GetXxxById(user_pb2.GetXxxByIdRequest(userId=user_id))


if __name__ == '__main__':
print(userService.get_mobile_by_id())

# 执行效果如下
(ENV) ➜ demo_python_convert_grpc python user_client.py
mobileId: "11166668888"
1
2
3
4
5
# 参数按照 user_pb2.py中方法Request定义的FieldDescriptor中具体的name来传入
fields=[
_descriptor.FieldDescriptor(
name='userId', full_name
...]

​ 至此,整体调用 gRPC接口 的流程算是跑通了,后续就看各自业务需求来定制化了。

总结

​ 最后吐槽下,很多时候准备做一件事的时候,发现网上教程挺多的,但是也会发现那些 “教程” 可能千篇一律。。。有些甚至一摸一样╮(╯Д╰)╭ 。。。当你真正按照这些 “教程” 来操作的时候又会发现可能不那么简单,所以说:实践是检验真理的唯一标准 !!!

------------- 本 文 结 束 感 谢 您 的 阅 读 -------------