前言
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
简介
gRPC是什么
在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
gRPC 客户端和服务端可以在多种环境中运行和交互 - 从 google 内部的服务器到你自己的笔记本,并且可以用任何 gRPC 支持的语言来编写。所以,你可以很容易地用 Java 创建一个 gRPC 服务端,用 Go、Python、Ruby 来创建客户端。此外,Google 最新 API 将有 gRPC 版本的接口,使你很容易地将 Google 的功能集成到你的应用里。
使用protocol buffers
gRPC 默认使用 protocol buffers
,这是 Google 开源的一套成熟的结构数据序列化机制
(当然也可以使用其他数据格式如 JSON)。正如你将在下方例子里所看到的,你用 proto files 创建 gRPC 服务,用 protocol buffers 消息类型来定义方法参数和返回类型。你可以在 Protocol Buffers 文档找到更多关于 Protocol Buffers 的资料。
安装
准备Python环境
1 | $ python -m pip install --upgrade pip |
安装gRPC
1 | # 在之前激活的虚拟环境下运行 |
同时还要安装gRPC tools:
1 | pip install grpcio-tools googleapis-common-protos |
示例
下载官方例子
1 | git clone -b v1.15.0 https://github.com/grpc/grpc |
运行一个gRPC应用
在 examples/python/helloworld
目录中:
- 运行服务端:
1 | python greeter_server.py |
- 在另一个terminal,运行客户端:
1 | python greeter_client.py |
Congratulations! You’ve just run a client-server application with gRPC.
python编写一个RPC服务完整过程
定义服务
创建我们例子的第一步是定义一个服务:一个 RPC 服务通过参数和返回类型来指定可以远程调用的方法。 gRPC 通过 protocol buffers
来实现。
我们使用 protocol buffers 接口定义语言来定义服务方法,用 protocol buffer 来定义参数和返回类型。客户端和服务端均使用服务定义生成的接口代码。
这里有我们服务定义的例子,在 helloworld.proto
里用 protocol buffers IDL 定义的。Greeter
服务有一个方法 SayHello
,可以让服务端从远程客户端接收一个包含用户名的 HelloRequest
消息后,在一个 HelloReply
里发送回一个 Greeter
。这是你可以在 gRPC 里指定的最简单的 RPC - 你可以在教程里找到针对你选择的语言更多类型的例子。
1 | syntax = "proto3"; |
生成gRPC
一旦定义好服务,我们可以使用 protocol buffer 编译器 protoc 来生成创建应用所需的特定客户端和服务端的代码 - 你可以生成任意 gRPC 支持的语言的代码,当然 PHP 和 Objective-C 仅支持创建客户端代码。生成的代码同时包括客户端的存根和服务端要实现的抽象接口,均包含 Greeter 所定义的方法。
1 | python -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. helloworld.proto |
这生成了 helloworld_pb2.py
和helloworld_pb2_grpc.py
两个文件 ,包含我们生成的客户端和服务端类,此外还有用于填充、序列化、提取 HelloRequest 和 HelloResponse 消息类型的类。
编写服务器端代码
服务实现
greeter_server.py
实现了 Greeter
服务所需要的行为。
正如你所见,Greeter 类通过实现 sayHello
方法,实现了从 proto 服务定义生成的helloworld_pb2.BetaGreeterServicer
接口:
1 | class Greeter(helloworld_pb2.BetaGreeterServicer): |
为了返回给客户端应答并且完成调用:
用我们的激动人心的消息构建并填充一个在我们接口定义的 HelloReply
应答对象。将 HelloReply 返回给客户端。
服务端实现
需要提供一个 gRPC 服务的另一个主要功能是让这个服务实在在网络上可用。
greeter_server.py 提供了以下代码作为 Python 的例子。
1 | server = helloworld_pb2.beta_create_Greeter_server(Greeter()) |
在这里我们创建了合理的 gRPC 服务器,将我们实现的 Greeter 服务绑定到一个端口。然后我们启动服务器:服务器现在已准备好从 Greeter 服务客户端接收请求。我们将在具体语言对应的文档里更深入地了解这所有的工作是怎样进行的。
编写客户端代码
连接服务
首先我们看一下我们如何连接 Greeter 服务器。我们需要创建一个 gRPC 频道,指定我们要连接的主机名和服务器端口。然后我们用这个频道创建存根实例。
1 | channel = implementations.insecure_channel('localhost', 50051) |
调用 RPC
现在我们可以联系服务并获得一个 greeting :
- 我们创建并填充一个 HelloRequest 发送给服务。
- 我们用请求调用存根的 SayHello(),如果 RPC 成功,会得到一个填充的 HelloReply ,从其中我们可以获得 greeting。
1 | response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'), _TIMEOUT_SECONDS) |
greeter_client.py
完整代码如下:
1 | import grpc |
运行并测试服务
你可以尝试用同一个语言在客户端和服务端构建并运行例子。或者你可以尝试 gRPC 最有用的一个功能 - 不同的语言间的互操作性,即在不同的语言运行客户端和服务端。每个服务端和客户端使用从同一过 proto 文件生成的接口代码,则意味着任何 Greeter 客户端可以与任何 Greeter 服务端对话。
- 运行服务端程序,程序会监听 50051端口:
1 | python route_guide_server.py |
- 运行客户端程序
1 | python route_guide_client.py |
grpc: 4种通信方式
helloworld 使用了最简单的 grpc 通信方式: 类似 http 协议的一次 request+response.
4种通信方式
根据不同的业务场景, grpc 支持 4 种通信方式:
- 客服端一次请求, 服务器一次应答
- 客服端一次请求, 服务器多次应答(流式)
- 客服端多次请求(流式), 服务器一次应答
- 客服端多次请求(流式), 服务器多次应答(流式)
官方提供了一个 route guide service 的 demo, 应用到了这 4 种通信方式, 具体的业务如下:
- 数据源: json 格式的数据源, 存储了很多地点, 每个地点由经纬度(point)和地名(location)组成
- 通信方式 1: 客户端请求一个地点是否在数据源中
- 通信方式 2: 客户端指定一个矩形范围(矩形的对角点坐标), 服务器返回这个范围内的地点信息
- 通信方式 3: 客户端给服务器发送多个地点信息, 服务器返回汇总信息(summary)
- 通信方式 4: 客户端和服务器使用地点信息 聊天(chat)
对应的proto文件
1 | syntax = "proto3"; |
proto 中想要表示流式传输, 只需要添加 stream
关键字即可
同样的, 使用 protoc
生成代码:
1 | python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. route_guide.proto |
生成了 route_guide_pb2.py
和route_guide_pb2_grpc.py
文件
处理数据源文件(route_guide_db.json)
1 | import json |
处理 json 的过程很简单, 解析 json 数据得到由坐标点组成的数组
怎么处理流式数据呢?. 答案是 for ... in
+ yield
完整服务器端代码
1 | """The Python implementation of the gRPC route guide server.""" |
完整客户端代码
1 | """The Python implementation of the gRPC route guide client.""" |
运行结果
1 | -------------- GetFeature -------------- |
小结
本文主要介绍了grpc下python的基本运行方式,以及grpc的四种通信方式。