一个简单的python gRpc (route_guide_si

2021-05-03  本文已影响0人  飞跑的蛤蟆

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  # 服务端

代码说明

创建服务器

实现 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()

serverstart() 方法是非阻塞的。将实例化一个新线程来处理请求。在此期间,调用 server.start() 的线程通常不会执行任何其他工作。

在这种情况下,您可以调用 server.wait_for_termination() 来彻底清除调用线程,直到服务器终止。

创建客户端

route_guide_client

创建存根

调用服务方法之前,我们要先创建一个存根。

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

生成的 Stub 类由 gRPC 客户端使用。它具有一个构造函数,它使用grpc. Channel对象并初始化存根。对于服务中的每个方法,初始化器向具有相同名称的存根对象添加对应的属性。根据RPC类型(一元或流式),该属性的值将是UnaryUnaryMultiCallable,UnaryStreamMultiCallable,StreamUnaryMultiCallable或StreamStreamMultiCallable类型的可调用对象。

Servicer

对于每个服务,将生成一个 Servicer 类,作为服务实现的超类。对于服务中的每个方法,将在 Servicer 类中生成对应的函数。使用服务实现重写此函数。.proto 文件中注释与代码元素相关联,在生成的 python 代码中以 docstring 的形式出现。

Registration Function

对于每个服务,都会生成一个函数,该函数在grpc. Server对象上注册一个实现该服务的Servicer对象,以便服务器可以将查询路由到相应的服务器。这个函数接受一个实现 Servicer 的对象,通常是上面描述的生成的 Servicer 代码元素的一个子类的实例,以及一个 grpc. Server 对象。

上一篇下一篇

猜你喜欢

热点阅读