文章导读
本文主要介绍gRPC中重要的部分Protocol Buffers语法,预计需要花费15分钟左右的时间,阅读后你将收获以下知识,
- Protocol Buffers语法规范。
- 通过Protocol Buffers进行数据结构定义。
- 通过Protocol Buffers进行服务定义
- 多种数据类型的gRPC服务搭建。
这是一篇进阶版本的文章,读者应具备基本的gRPC基础,在此之前已有文章讲解Python3搭建gRPC(入门版),如不了解gRPC建议先阅读
同时提供以下进阶材料
- Python3搭建gRPC(入门版)
- 同步视频教程(后续会更新)
- 其他相关联文章,包括pytest进行gRPC测试,python3客户端测试java/golang grpc服务端(后续会更新)
如果大家觉得有收获,记得一键三连,后续继续为大家输出更多有价值的文章!!!
Protocal Buffers语法
Protocal Buffers是描述型语言,语法简单,本章将详细讲解
数据类型和各语言之间的关系
Protocal Buffers最终都是需要转换成各种语言的源码,需要了解在pb的数据类型和各语言之间的对应关系
.proto Type | Notes | C++ Type | Java/Kotlin Type[1] | Python Type[3] | Go Type | Ruby Type | C# Type | PHP Type | Dart Type |
---|---|---|---|---|---|---|---|---|---|
double | double | double | float | float64 | Float | double | float | double | |
float | float | float | float | float32 | Float | float | float | double | |
int32 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int | |
int64 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 | |
uint32 | uint32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int | |
uint64 | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 | |
sint32 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int | |
sint64 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 | |
fixed32 | uint32 | int[2] | int/long[4] | uint32 | Fixnum or Bignum (as required) | uint | integer | int | |
fixed64 | uint64 | long[2] | int/long[4] | uint64 | Bignum | ulong | integer/string[6] | Int64 | |
sfixed32 | int32 | int | int | int32 | Fixnum or Bignum (as required) | int | integer | int | |
sfixed64 | int64 | long | int/long[4] | int64 | Bignum | long | integer/string[6] | Int64 | |
bool | bool | boolean | bool | bool | TrueClass/FalseClass | bool | boolean | bool | |
string | string | String | str/unicode[5] | string | String (UTF-8) | string | string | String | |
bytes | string | ByteString | str (Python 2) bytes (Python 3) | []byte | String (ASCII-8BIT) | ByteString | string |
常见类型如何定义
pb包含有多种类型,包括int,float,string,bool,map等
pb没有list类型,该如何定义,在list类型章节介绍
基础类型
syntax = "proto3";
message IntRequest{
int32 id=1;
}
message FloatRequest{
float num=1;
}
message StringRequest{
string name=1;
}
message BoolRequest{
bool exist=1;
}
map类型
map类型需要定义内部的泛型,如下表示search为key和value都是string类型的map,在Python中叫字典
message MapRequest{
map<string, string> search=1;
}
list类型
list在pb中没有对应的数据类型,需要使用repeated关键词的意思是可重复0到N个相同的值,和列表的定义是一致的,列表中有0到N元素
如下定义表示UsersRequest数据结构中,usernames是一个string类型的列表
message UsersRequest{
repeated string usernames=1;
}
外部依赖的类型怎么处理
已有一个map_demo.proto
# map_demo.proto
syntax = "proto3";
message MapDemo{
map<string, string> keyValue=1;
}
map_use_demo.proto需要引用map_demo.proto中定义的类型
# map_use_demo.proto
syntax = "proto3";
import "map_demo.proto";
service MapService{
rpc MapHello(Request) returns (Response){}
}
message Request{
MapDemo mapDemo=1;
}
message Response{
string name=1;
}
需要在map_use_demo.proto中import "map_demo.proto"
,这样map_demo中定义的MapDemo数据类型就可以使用了
外部引用有2个注意点:
1、在第四章中讲到grpc_tools.protoc -I参数就是在这种情况下使用,-I需要指定为map_demo.proto所在的目录,如不指定,生成map_use_demo时会找不到map_demo.proto
2、map_demo.proto也需要编译成Python代码
具体demo实例请参考第6章
message定义
message定义一个数据结构,类似于Python的类
message User{
string name=1; # 从1开始依次往后
string password=2; # 2
}
message 类名{
类型 变量名=1; # 这里1是固定值,从1开始依次往后
类型 变量名=2; # 这里1是固定值,从1开始依次往后
}
service定义
定义服务相对简单,通过关键字service配合关键字rpc可以进行服务定义
returns定义接口返回的数据类型
service MapService{
rpc MapHello(Request) returns (Response){}
}
service 服务名{
rpc 接口名(请求类型) returns (返回类型){}
}
pb转成源码作用解释
常见gRPC实战
将通过2个实际案例讲解复杂的pb定义
实战一:多种数据类型的复杂接口
- 第一步proto定义,
# all_demo.proto
syntax = "proto3";
service UserService{
rpc GetUserByKeyWord(Request) returns(Response){}
}
message User{
string name=1;
int32 id=2;
float age=3;
bool isBoy=4;
}
message UserList{
repeated User user=1;
}
message Response{
UserList userList=1;
}
message Request{
map<string,string> keyWord=1;
bool fuzzy=2;
}
2.转成Python源码
python3 -m grpc_tools.protoc -I ./ --python_out=. --pyi_out=. --grpc_python_out=. all_demo.proto
3.开发服务端
import grpc
from concurrent import futures
from grpc_demo.pb.all_demo_pb2_grpc import UserServiceServicer, add_UserServiceServicer_to_server
from grpc_demo.pb.all_demo_pb2 import Request, Response, User, UserList
class UserService(UserServiceServicer):
def GetUserByKeyWord(self, request, context):
user1 = User(name="zhangsan", id=1, age=24.3, isBoy=False)
user2 = User(name="zhangsan1", id=1, age=24.3, isBoy=False)
user_list = UserList(user=[user1, user2])
response = Response(userList=user_list)
return response
def serve():
port = "50000"
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
add_UserServiceServicer_to_server(UserService(), server)
server.add_insecure_port('[::]:' + port)
server.start()
print("Server started, listening on " + port)
server.wait_for_termination()
if __name__ == '__main__':
serve()
4.开发客户端
import grpc
from grpc_demo.pb.all_demo_pb2_grpc import UserServiceStub
from grpc_demo.pb.all_demo_pb2 import Request
def run():
with grpc.insecure_channel("localhost:50000") as channel:
stub = UserServiceStub(channel)
key_word = {"name": "zhang"}
request = Request(keyWord=key_word, fuzzy=False)
resp = stub.GetUserByKeyWord(request)
print(resp)
if __name__ == '__main__':
run()
5.通过客户端调试
实战二:存在外部依赖的场景
1、定义第一个proto,map_demo.proto
这个proto定义了MapDemo的类
#map_demo.proto
syntax = "proto3";
message MapDemo{
map<string, string> keyValue=1;
}
2、定义第二个proto,这个proto会引用第一个proto中定义的类
这个proto定义了接口,以及Request类,Request类引用了第一个proto中的MapDemo
syntax = "proto3";
import "map_demo.proto";
service MapService{
rpc MapHello(Request) returns (Response){}
}
message Request{
MapDemo mapDemo=1;
}
message Response{
string name=1;
}
3、将map_demo.proto转换成python代码
这一步很重要,虽然map_demo是被use_map_demo引用,但也需要生成python代码,否则会报错
python3 -m grpc_tools.protoc -I ./ --python_out=. --pyi_out=. --grpc_python_out=. map_demo.proto
4、将use_map_demo.proto转换成python代码
python3 -m grpc_tools.protoc -I ./ --python_out=. --pyi_out=. --grpc_python_out=. map_use_demo.proto
注意:这行完成上述操作后会生成如下python代码
5、gRPC服务代码
from concurrent import futures
import grpc
from grpc_demo.pb.map_use_demo_pb2_grpc import MapServiceServicer, add_MapServiceServicer_to_server
from grpc_demo.pb.map_use_demo_pb2 import Response
class MapUseDemoServer(MapServiceServicer):
def MapHello(self, request, context):
print(request)
return Response(name="hello map")
def serve():
port = "40001"
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
add_MapServiceServicer_to_server(MapUseDemoServer(), server)
server.add_insecure_port('[::]:' + port)
server.start()
print("Server started, listening on " + port)
server.wait_for_termination()
if __name__ == '__main__':
serve()
6、客户端代码
import grpc
from grpc_demo.pb.map_use_demo_pb2_grpc import MapServiceStub
from grpc_demo.pb.map_use_demo_pb2 import Request
from grpc_demo.pb.map_demo_pb2 import MapDemo
def run():
with grpc.insecure_channel("localhost:40001") as channel:
stub = MapServiceStub(channel)
map_demo = MapDemo(keyValue={"hello": "a"})
response = stub.MapHello(Request(mapDemo=map_demo))
print(response.name)
if __name__ == '__main__':
run()
7、启动服务端,通过客户端调用查看结果
客户端打印hello,表示调用成功
服务端打印request信息
常见错误
外部依赖没有转换
错误信息:ModuleNotFoundError: No module named ‘grpc_demo.pb.map_demo_pb2’
这个错误原因是use_map_demo.proto引用了map_demo.proto,但map_demo.proto没有转换成pyton代码导致引入失败
解决方案:将map_demo.proto转换成python代码
python3 -m grpc_tools.protoc -I ./ --python_out=. --pyi_out=. --grpc_python_out=. map_demo.proto
客户端入参类型错误
ValueError: Protocol message MapDemo has no “xxxx” field.
错误演示:Request的参数应该是mapDemo类型,不是一个字典
正确做法:定义map_demo的对象,入参给Request
本文转自 https://juejin.cn/post/7210375037122101308,如有侵权,请联系删除。
题外话
当下这个大数据时代不掌握一门编程语言怎么跟的上脚本呢?当下最火的编程语言Python前景一片光明!如果你也想跟上时代提升自己那么请看一下.
感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。
👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)
一、Python所有方向的学习路线
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。
二、Python必备开发工具
工具都帮大家整理好了,安装就可直接上手!
三、最新Python学习笔记
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、Python视频合集
观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
五、实战案例
纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
六、面试宝典
简历模板
👉CSDN大礼包🎁:全网最全《Python学习资料》免费赠送🆓!(安全链接,放心点击)