生成的代码参考

生成的代码参考

gRPC Python 依赖于 Protocol Buffers 编译器(protoc)来生成代码。它使用一个插件来补充纯 protoc 生成的代码,添加 gRPC 特定的代码。对于包含 gRPC 服务的 .proto 服务描述,纯 protoc 生成的代码会合成到一个 _pb2.py 文件中,而 gRPC 特定的代码则位于一个 _pb2_grpc.py 文件中。后者 Python 模块会导入前者。本页面重点介绍生成的代码中 gRPC 特定的子集。

示例

考虑以下 FortuneTeller proto 服务

service FortuneTeller {
  // Returns the horoscope and zodiac sign for the given month and day.
  rpc TellFortune(HoroscopeRequest) returns (HoroscopeResponse) {
    // errors: invalid month or day, fortune unavailable
  }

  // Replaces the fortune for the given zodiac sign with the provided one.
  rpc SuggestFortune(SuggestionRequest) returns (SuggestionResponse) {
    // errors: invalid zodiac sign
  }
}

编译该服务时,gRPC protoc 插件会生成类似于以下 _pb2_grpc.py 文件的代码

import grpc

import fortune_pb2

class FortuneTellerStub(object):

  def __init__(self, channel):
    """Constructor.

    Args:
      channel: A grpc.Channel.
    """
    self.TellFortune = channel.unary_unary(
        '/example.FortuneTeller/TellFortune',
        request_serializer=fortune_pb2.HoroscopeRequest.SerializeToString,
        response_deserializer=fortune_pb2.HoroscopeResponse.FromString,
        )
    self.SuggestFortune = channel.unary_unary(
        '/example.FortuneTeller/SuggestFortune',
        request_serializer=fortune_pb2.SuggestionRequest.SerializeToString,
        response_deserializer=fortune_pb2.SuggestionResponse.FromString,
        )


class FortuneTellerServicer(object):

  def TellFortune(self, request, context):
    """Returns the horoscope and zodiac sign for the given month and day.
    errors: invalid month or day, fortune unavailable
    """
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')

  def SuggestFortune(self, request, context):
    """Replaces the fortune for the given zodiac sign with the provided
one.
    errors: invalid zodiac sign
    """
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')


def add_FortuneTellerServicer_to_server(servicer, server):
  rpc_method_handlers = {
      'TellFortune': grpc.unary_unary_rpc_method_handler(
          servicer.TellFortune,
          request_deserializer=fortune_pb2.HoroscopeRequest.FromString,
          response_serializer=fortune_pb2.HoroscopeResponse.SerializeToString,
      ),
      'SuggestFortune': grpc.unary_unary_rpc_method_handler(
          servicer.SuggestFortune,
          request_deserializer=fortune_pb2.SuggestionRequest.FromString,
          response_serializer=fortune_pb2.SuggestionResponse.SerializeToString,
      ),
  }
  generic_handler = grpc.method_handlers_generic_handler(
      'example.FortuneTeller', rpc_method_handlers)
  server.add_generic_rpc_handlers((generic_handler,))

代码元素

gRPC 生成的代码首先导入 grpc 包和纯 _pb2 模块(由 protoc 合成),该模块定义了非 gRPC 特定的代码元素,例如与 Protocol Buffers 消息相对应的类以及反射使用的描述符。

对于 .proto 文件中的每个服务 Foo,会生成三个主要元素

  • Stub: FooStub 供客户端用于连接 gRPC 服务。
  • Servicer: FooServicer 供服务端用于实现 gRPC 服务。
  • 注册函数: add_FooServicer_to_server 函数用于向 grpc.Server 对象注册 Servicer。

Stub

生成的 Stub 类由 gRPC 客户端使用。它有一个构造函数,接受一个 grpc.Channel 对象并初始化 Stub。对于服务中的每个方法,初始化程序都会向 Stub 对象添加一个同名属性。根据 RPC 类型(一元或流式),该属性的值将是类型为 UnaryUnaryMultiCallable, UnaryStreamMultiCallable, StreamUnaryMultiCallableStreamStreamMultiCallable 的可调用对象。

Servicer

对于每个服务,都会生成一个 Servicer 类,作为服务实现的超类。对于服务中的每个方法,都会在 Servicer 类中生成一个相应的函数。使用服务实现覆盖此函数。与 .proto 文件中代码元素关联的注释会作为文档字符串出现在生成的 Python 代码中。

注册函数

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