gRPC是由Google公司开源的高性能RPC框架。python
gRPC支持多语言web
gRPC原生使用C、Java、Go进行了三种实现,而C语言实现的版本进行封装后又支持C++、C#、Node、ObjC、 Python、Ruby、PHP等开发语言服务器
gRPC支持多平台架构
支持的平台包括:Linux、Android、iOS、MacOS、Windows框架
gRPC的消息协议使用Google自家开源的Protocol Buffers协议机制(proto3) 序列化ide
gRPC的传输使用HTTP/2标准,支持双向流和链接多路复用性能
C语言实现的gRPC支持多语言,其架构以下ui
安装编码
pip install grpc
install grpcio-tools pip
使用url
.proto
为后缀名)中。
Protocol Buffers 是一种与语言无关,平台无关的可扩展机制,用于序列化结构化数据。使用Protocol Buffers 能够一次定义结构化的数据,而后可使用特殊生成的源代码轻松地在各类数据流中使用各类语言编写和读取结构化数据。
如今有许多框架等在使用Protocol Buffers。gRPC也是基于Protocol Buffers。 Protocol Buffers 目前有2和3两个版本号。
在gRPC中推荐使用proto3版本。
Protocol Buffers文档的第一行非注释行,为版本申明,不填写的话默认为版本2。
syntax = "proto3"; 或者 syntax = "proto2";
Protocol Buffers 能够声明package,来防止命名冲突。 Packages是可选的。
package foo.bar; message Open { ... }
使用的时候,也要加上命名空间,
message Foo { ... foo.bar.Open open = 1; ... }
注意:对于Python而言,package
会被忽略处理,由于Python中的包是以文件目录来定义的。
Protocol Buffers 中能够导入其它文件消息等,与Python的import相似。
import “myproject/other_protos.proto”;
消息messge是用来定义数据的,服务service是用来gRPC的方法的。
Protocol Buffers 提供如下两种注释方式。
// 单行注释 /* 多行注释 多行注释 */
.proto | 说明 | Python |
---|---|---|
double | float | |
float | float | |
int32 | 使用变长编码,对负数编码效率低, 若是你的变量多是负数,可使用sint32 | int |
int64 | 使用变长编码,对负数编码效率低,若是你的变量多是负数,可使用sint64 | int/long |
uint32 | 使用变长编码 | int/long |
uint64 | 使用变长编码 | int/long |
sint32 | 使用变长编码,带符号的int类型,对负数编码比int32高效 | int |
sint64 | 使用变长编码,带符号的int类型,对负数编码比int64高效 | int/long |
fixed32 | 4字节编码, 若是变量常常大于2^{28} 的话,会比uint32高效 | int |
fixed64 | 8字节编码, 若是变量常常大于2^{56} 的话,会比uint64高效 | int/long |
sfixed32 | 4字节编码 | int |
sfixed64 | 8字节编码 | int/long |
bool | bool | |
string | 必须包含utf-8编码或者7-bit ASCII text | str |
bytes | 任意的字节序列 | str |
在 Proto Buffers 中,咱们能够定义枚举和枚举类型,
enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } Corpus corpus = 4;
枚举定义在一个消息内部或消息外部都是能够的,若是枚举是 定义在 message 内部,而其余 message 又想使用,那么能够经过 MessageType.EnumType 的方式引用。
定义枚举的时候,咱们要保证第一个枚举值必须是0,枚举值不能重复,除非使用 option allow_alias = true 选项来开启别名。
enum EnumAllowingAlias { option allow_alias = true; UNKNOWN = 0; STARTED = 1; RUNNING = 1; }
枚举值的范围是32-bit integer,但由于枚举值使用变长编码,因此不推荐使用负数做为枚举值,由于这会带来效率问题。
Protocol Buffers使用message定义消息数据。在Protocol Buffers中使用的数据都是经过message消息数据封装基本类型数据或其余消息数据,对应Python中的类。
message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; }
消息定义中的每一个字段都有惟一的编号。这些字段编号用于以消息二进制格式标识字段,而且在使用消息类型后不该更改。 请注意,1到15范围内的字段编号须要一个字节进行编码,包括字段编号和字段类型。16到2047范围内的字段编号占用两个字节。所以,您应该为很是频繁出现的消息元素保留数字1到15。请记住为未来可能添加的经常使用元素留出一些空间。
最小的标识号能够从1开始,最大到2^29 - 1,或 536,870,911。不可使用其中的[19000-19999]的标识号, Protobuf协议实现中对这些进行了预留。若是非要在.proto文件中使用这些预留标识号,编译时就会报警。一样你也不能使用早期保留的标识号。
消息字段能够是如下之一:
singular:格式良好的消息能够包含该字段中的零个或一个(但不超过一个)。
repeated:此字段能够在格式良好的消息中重复任意次数(包括零)。将保留重复值的顺序。对应Python的列表。
message Result { string url = 1; string title = 2; repeated string snippets = 3; }
能够在单个.proto文件中定义多个消息类型。
message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; } message SearchResponse { ... }
保留变量不被使用
若是经过彻底删除字段或将其注释来更新消息类型,则将来用户能够在对类型进行本身的更新时重用字段编号。若是之后加载相同的旧版本,这可能会致使严重问题,包括数据损坏,隐私错误等。确保不会发生这种状况的一种方法是指定已删除字段的字段编号(或名称)reserved。若是未来的任何用户尝试使用这些字段标识符,protobuf编译器将会报错。
message Foo { reserved 2, 15, 9 to 11; reserved "foo", "bar"; }
解析消息时,若是编码消息不包含特定的单数元素,则解析对象中的相应字段将设置为该字段的默认值。这些默认值是特定于类型的:
你能够在其余消息类型中定义、使用消息类型,在下面的例子中,Result消息就定义在SearchResponse消息内,如:
message SearchResponse { message Result { string url = 1; string title = 2; repeated string snippets = 3; } repeated Result results = 1; }
若是要在其父消息类型以外重用此消息类型,使用
SearchResponse.Result
若是要在数据定义中建立关联映射,Protocol Buffers提供了一种方便的语法:
map< key_type, value_type> map_field = N ;
其中key_type能够是任何整数或字符串类型。请注意,枚举不是有效的key_type。value_type能够是除map映射类型外的任何类型。
例如,若是要建立项目映射,其中每条Project消息都与字符串键相关联,则能够像下面这样定义它:
map<string, Project> projects = 3 ;
若是你的消息中有不少可选字段, 而且同时至多一个字段会被设置, 你能够增强这个行为,使用oneof特性节省内存。
为了在.proto定义oneof字段, 你须要在名字前面加上oneof关键字, 好比下面例子的test_oneof:
message SampleMessage { oneof test_oneof { string name = 4; SubMessage sub_message = 9; } }
而后你能够增长oneof字段到 oneof 定义中. 你能够增长任意类型的字段, 可是不能使用repeated 关键字。
Protocol Buffers使用service定义RPC服务。
示例:
message HelloRequest { string greeting = 1; } message HelloResponse { string reply = 1; } service HelloService { rpc SayHello (HelloRequest) returns (HelloResponse) {} } syntax = "proto3"; message UserRequest { string user_id=1; int32 channel_id=2; int32 article_num=3; int64 time_stamp=4; } message Track { string click=1; string collect=2; string share=3; string read=4; } message Article { int64 article_id=1; Track track=2; } message ArticleResponse { string exposure=1; int64 time_stamp=2; repeated Article recommends=3; } service UserRecommend { rpc user_recommend(UserRequest) returns(ArticleResponse) {} }
编译生成代码
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. reco.proto
-I
表示搜索proto文件中被导入文件的目录--python_out
表示保存生成Python文件的目录,生成的文件中包含接口定义中的数据类型--grpc_python_out
表示保存生成Python文件的目录,生成的文件中包含接口定义中的服务类型在toutiao-backend/common/rpc目录下执行上述命令,会自动生成以下两个rpc调用辅助代码模块: