route_guide_simple
这是一个简化版的route_guide,本文参考自gRpc官方文档,略有删改,水平有限翻译不对的地方敬请见谅。
# 生成_pb2.py和_pb2_grpc.py文件
python -m grpc_tools.protoc -I ./ --python_out . --grpc_python_out . ./route_guide.proto
文件结构以及说明
./
├── README.md
├── route_guide_client.py # 客户端
├── route_guide_db.json # 提供数据
├── route_guide_pb2_grpc.py # 生成的
├── route_guide_pb2.py # 生成的
├── route_guide.proto # protocol buffer file
├── route_guide_resources.py # 提供数据
└── route_guide_server.py # 服务端
-
route_guide_client.py
和route_guide_server.py
文件分别为客户端和服务端,它们的通信是通过gRpc实现的。 -
route_guide_db.json
和route_guide_resources.py
文件是提供数据的。 -
route_guide.proto
文件是protocal buffer file,protobuf是一种接口描述语言(IDL),里面定义了语言无关的数据结构以供其他语言调用。 -
route_guide_pb2.py
和route_guide_pb2_grpc.py
是通过protobuf生成的-
route_guide_pb2
定义了messages的数据结构 -
route_guide_pb2_grpc
- 客户端可以通过
RouteGuideStub
调用RouteGuide
RPC -
RouteGuideServicer
定义了RouteGuide
服务的实现接口 -
add_RouteGuideServicer_to_server
将RouteGuideServicer
添加到grpc.Server
- 客户端可以通过
-
代码说明
创建服务器
实现 RouteGuide
:
gRpc/examples/route_guide_simple/route_guide_server.py
模块中有一个 RouteGuideServicer
类,它继承自 route_guide_pb2_grpc. RouteGuideServicer
,并实现了所有 RouteGuide
方法。
# RouteGuideServicer provides an implementation of the methods of the RouteGuide service.
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):
Simple RPC
GetFeature是一个最简单的类型,它仅从客户端获取Point,然后在Feature中返回数据库中相应的feature信息。
def GetFeature(self, request, context):
feature = get_feature(self.db, request)
if feature is None:
return route_guide_pb2.Feature(name="", location=request)
else:
return feature
该方法传递了 route_guide_pb2. Point
请求给RPC,以及一个 grpc. ServicerContext
对象,它提供了RPC特定信息(例如超时限制)。返回一个 route_guide_pb2. Feature
响应。
gRpc服务器
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
RouteGuideServicer(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
server
的 start()
方法是非阻塞的。将实例化一个新线程来处理请求。在此期间,调用 server.start()
的线程通常不会执行任何其他工作。
在这种情况下,您可以调用 server.wait_for_termination()
来彻底清除调用线程,直到服务器终止。
创建客户端
创建存根
调用服务方法之前,我们要先创建一个存根。
grpc.insecure_channel('localhost:50051')
stub = route_guide_pb2_grpc.RouteGuideStub(channel)
我们实例化一个 route_guide_pb2_grpc
模块的 RouteGuideStub
类,它是由 protobuf
文件生成的。
调用服务方法
同步:对于返回单一响应的RPC方法 ("response-unary" methods)
,gRPC Python支持同步(阻塞)和异步(非阻塞)控制流语义。同步调用简单的RPC方法 GetFeature
几乎与调用本地方法一样直接。 RPC调用等待服务器响应,并且将返回响应或引发异常:
feature = stub.GetFeature(point)
异步:对于 GetFeature
的异步调用与此类似,就像在线程池中异步调用本地方法一样:
feature_future = stub.GetFeature.future(point)
feature = feature_future.result()
启动服务
# 启动服务端
python route_guide_server.py
# 启动客户端
python route_guide_client.py
客户端打印结果:
-------------- GetFeature --------------
Feature called Berkshire Valley Management Area Trail, Jefferson, NJ, USA at latitude: 409146138
longitude: -746188906
Found no feature at
protoc生成的代码
gRpc Python 依赖于协议缓冲编译器(protoc)来生成代码。它使用插件通过 gRPC-specific
代码通过 plain protoc
来补充所生成的代码。对于包含gRPC服务的.proto服务描述, plain protoc
生成的代码在_pb2.py文件中合成,而 gRPC-specific
代码在_pb2_grpc.py文件中。后者的 python 模块导入前者。
示例
syntax = "proto3";
package route_guide_simple;
// the route guide service
service RouteGuide{
// A simple RPC
rpc GetFeature (Point) returns (Feature) {}
}
message Point{
int32 latitude = 1;
int32 longitude = 2;
}
message Feature{
string name = 1;
Point location = 2;
}
当服务被编译时,gRPC 的protoc 插件会生成类似于下面的 _pb2_grpc.py 文件的代码:
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
"""Client and server classes corresponding to protobuf-defined services."""
import grpc
import route_guide_pb2 as route__guide__pb2
class RouteGuideStub(object):
"""the route guide service
"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.GetFeature = channel.unary_unary(
'/route_guide_simple.RouteGuide/GetFeature',
request_serializer=route__guide__pb2.Point.SerializeToString,
response_deserializer=route__guide__pb2.Feature.FromString,
)
class RouteGuideServicer(object):
"""the route guide service
"""
def GetFeature(self, request, context):
"""A simple RPC
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details('Method not implemented!')
raise NotImplementedError('Method not implemented!')
def add_RouteGuideServicer_to_server(servicer, server):
rpc_method_handlers = {
'GetFeature': grpc.unary_unary_rpc_method_handler(
servicer.GetFeature,
request_deserializer=route__guide__pb2.Point.FromString,
response_serializer=route__guide__pb2.Feature.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'route_guide_simple.RouteGuide', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class RouteGuide(object):
"""the route guide service
"""
@staticmethod
def GetFeature(request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
insecure=False,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None):
return grpc.experimental.unary_unary(request, target, '/route_guide_simple.RouteGuide/GetFeature',
route__guide__pb2.Point.SerializeToString,
route__guide__pb2.Feature.FromString,
options, channel_credentials,
insecure, call_credentials, compression, wait_for_ready, timeout, metadata)
Code Elements
gRPC 生成的代码首先导入 grpc 包和由 protoc 合成的纯 _pb2 模块,后者定义了非 gRPC 特定的代码元素,比如对应于协议缓冲消息的类和反射使用的描述符。
对于.proto 文件中的每个服务 Foo,会生成三个主要元素:
- Stub:
RouteGuideStub
被客户端用于连接gRpc
服务 - Servicer:
RouteGuideServicer
被服务端用于实现gRpc
服务 - Registration Function:
add_RouteGuideServicer_to_server
函数用于向grpc. Server
对象注册服务者。
Stub
生成的 Stub 类由 gRPC 客户端使用。它具有一个构造函数,它使用grpc. Channel对象并初始化存根。对于服务中的每个方法,初始化器向具有相同名称的存根对象添加对应的属性。根据RPC类型(一元或流式),该属性的值将是UnaryUnaryMultiCallable,UnaryStreamMultiCallable,StreamUnaryMultiCallable或StreamStreamMultiCallable类型的可调用对象。
Servicer
对于每个服务,将生成一个 Servicer 类,作为服务实现的超类。对于服务中的每个方法,将在 Servicer 类中生成对应的函数。使用服务实现重写此函数。.proto 文件中注释与代码元素相关联,在生成的 python 代码中以 docstring 的形式出现。
Registration Function
对于每个服务,都会生成一个函数,该函数在grpc. Server对象上注册一个实现该服务的Servicer对象,以便服务器可以将查询路由到相应的服务器。这个函数接受一个实现 Servicer 的对象,通常是上面描述的生成的 Servicer 代码元素的一个子类的实例,以及一个 grpc. Server 对象。