在python中使用grpc和protobuf

3 篇文章 0 订阅
3 篇文章 0 订阅

简介

在python中使用grpc和protobuf,比java和c#中使用要简单一些。只需要先安装grpcio包,然后就可以应用了。

安装

使用pip安装grpcio依赖包;

$ pip install grpcio
Collecting grpcio
  Downloading grpcio-1.7.0-cp27-cp27m-macosx_10_10_intel.whl (1.5MB)
    100% |████████████████████████████████| 1.5MB 18kB/s 
Requirement already satisfied: enum34>=1.0.4 in /Users/David/anaconda2/lib/python2.7/site-packages (from grpcio)
Requirement already satisfied: futures>=2.2.0 in /Users/David/anaconda2/lib/python2.7/site-packages (from grpcio)
Requirement already satisfied: six>=1.5.2 in /Users/David/anaconda2/lib/python2.7/site-packages (from grpcio)
Collecting protobuf>=3.3.0 (from grpcio)
  Downloading protobuf-3.5.0-py2.py3-none-any.whl (388kB)
    100% |████████████████████████████████| 389kB 32kB/s 
Requirement already satisfied: setuptools in /Users/David/anaconda2/lib/python2.7/site-packages (from protobuf>=3.3.0->grpcio)
Installing collected packages: protobuf, grpcio
Successfully installed grpcio-1.7.0 protobuf-3.5.0

安装时,自动地安装了protobuf工具包。

定义protobuf

下面定义一个简单的protobuf文件,在其中声明一个grpc服务。
创建一个proto目录,并在其中创建grpchello.proto文件,如下内容。

syntax = "proto3";
package grpcDemo;

message HelloRequest {
   string name = 1;
}

message HelloReply {
   string message = 1;
}


service gRPC {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

Note:
- 其实这个protobuf定义文件,在我们的java、c#版本示例中使用了,而且各版本的服务和客户端可以正常通行调用。

编译protobuf

使用protobuf的编译器,为我们生成python版本的Message定义和服务的架手脚。

python -m grpc_tools.protoc -I./proto --python_out=. --grpc_python_out=. grpchello.proto 

在当前目录下,生成2个文件:
- grpchello_pb2.py
- grpchello_pb2_grpc.py

可以看看这2个文件:

首先消息定义文件:

# Generated by the protocol buffer compiler.  DO NOT EDIT!
# source: grpchello.proto

import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
from google.protobuf import descriptor_pb2
# @@protoc_insertion_point(imports)

_sym_db = _symbol_database.Default()




DESCRIPTOR = _descriptor.FileDescriptor(
  name='grpchello.proto',
  package='grpcDemo',
  syntax='proto3',
  serialized_pb=_b('\n\x0fgrpchello.proto\x12\x08grpcDemo\"\x1c\n\x0cHelloRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"\x1d\n\nHelloReply\x12\x0f\n\x07message\x18\x01 \x01(\t2B\n\x04gRPC\x12:\n\x08SayHello\x12\x16.grpcDemo.HelloRequest\x1a\x14.grpcDemo.HelloReply\"\x00\x62\x06proto3')
)




_HELLOREQUEST = _descriptor.Descriptor(
  name='HelloRequest',
  full_name='grpcDemo.HelloRequest',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='name', full_name='grpcDemo.HelloRequest.name', index=0,
      number=1, type=9, cpp_type=9, label=1,
      has_default_value=False, default_value=_b("").decode('utf-8'),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=29,
  serialized_end=57,
)


_HELLOREPLY = _descriptor.Descriptor(
  name='HelloReply',
  full_name='grpcDemo.HelloReply',
  filename=None,
  file=DESCRIPTOR,
  containing_type=None,
  fields=[
    _descriptor.FieldDescriptor(
      name='message', full_name='grpcDemo.HelloReply.message', index=0,
      number=1, type=9, cpp_type=9, label=1,
      has_default_value=False, default_value=_b("").decode('utf-8'),
      message_type=None, enum_type=None, containing_type=None,
      is_extension=False, extension_scope=None,
      options=None),
  ],
  extensions=[
  ],
  nested_types=[],
  enum_types=[
  ],
  options=None,
  is_extendable=False,
  syntax='proto3',
  extension_ranges=[],
  oneofs=[
  ],
  serialized_start=59,
  serialized_end=88,
)

DESCRIPTOR.message_types_by_name['HelloRequest'] = _HELLOREQUEST
DESCRIPTOR.message_types_by_name['HelloReply'] = _HELLOREPLY
_sym_db.RegisterFileDescriptor(DESCRIPTOR)

HelloRequest = _reflection.GeneratedProtocolMessageType('HelloRequest', (_message.Message,), dict(
  DESCRIPTOR = _HELLOREQUEST,
  __module__ = 'grpchello_pb2'
  # @@protoc_insertion_point(class_scope:grpcDemo.HelloRequest)
  ))
_sym_db.RegisterMessage(HelloRequest)

HelloReply = _reflection.GeneratedProtocolMessageType('HelloReply', (_message.Message,), dict(
  DESCRIPTOR = _HELLOREPLY,
  __module__ = 'grpchello_pb2'
  # @@protoc_insertion_point(class_scope:grpcDemo.HelloReply)
  ))
_sym_db.RegisterMessage(HelloReply)



_GRPC = _descriptor.ServiceDescriptor(
  name='gRPC',
  full_name='grpcDemo.gRPC',
  file=DESCRIPTOR,
  index=0,
  options=None,
  serialized_start=90,
  serialized_end=156,
  methods=[
  _descriptor.MethodDescriptor(
    name='SayHello',
    full_name='grpcDemo.gRPC.SayHello',
    index=0,
    containing_service=None,
    input_type=_HELLOREQUEST,
    output_type=_HELLOREPLY,
    options=None,
  ),
])
_sym_db.RegisterServiceDescriptor(_GRPC)

DESCRIPTOR.services_by_name['gRPC'] = _GRPC

# @@protoc_insertion_point(module_scope)

在看看grpc服务定义

# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc

import grpchello_pb2 as grpchello__pb2


class gRPCStub(object):
  # missing associated documentation comment in .proto file
  pass

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

    Args:
      channel: A grpc.Channel.
    """
    self.SayHello = channel.unary_unary(
        '/grpcDemo.gRPC/SayHello',
        request_serializer=grpchello__pb2.HelloRequest.SerializeToString,
        response_deserializer=grpchello__pb2.HelloReply.FromString,
        )


class gRPCServicer(object):
  # missing associated documentation comment in .proto file
  pass

  def SayHello(self, request, context):
    # missing associated documentation comment in .proto file
    pass
    context.set_code(grpc.StatusCode.UNIMPLEMENTED)
    context.set_details('Method not implemented!')
    raise NotImplementedError('Method not implemented!')


def add_gRPCServicer_to_server(servicer, server):
  rpc_method_handlers = {
      'SayHello': grpc.unary_unary_rpc_method_handler(
          servicer.SayHello,
          request_deserializer=grpchello__pb2.HelloRequest.FromString,
          response_serializer=grpchello__pb2.HelloReply.SerializeToString,
      ),
  }
  generic_handler = grpc.method_handlers_generic_handler(
      'grpcDemo.gRPC', rpc_method_handlers)
  server.add_generic_rpc_handlers((generic_handler,))

简单看下:
- 在grpc服务架手脚定义中,定义了gRPCStub,这是给client端使用,调用grpc服务的。
- 定义的服务类gRPCServicer,方法SayHello需要我们在子类中进行实现。定义的add_gRPCServicer_to_server方法,用于把实现的类和grpc API调用注册起来。

这里使用的几个主要方法(类):
- grpc.server – Creates a Server with which RPCs can be serviced
- grpc.method_handlers_generic_handler – Creates a GenericRpcHandler from RpcMethodHandlers.
- grpc.unary_unary_rpc_method_handler – Creates an RpcMethodHandler for a unary-unary RPC method.

实现服务

在我们的实现服务的类中,使用服务方法,并在网络中暴露出来。

# -*- coding: utf-8 -*-
import grpc
import time
from concurrent import futures 
import grpchello_pb2, grpchello_pb2_grpc

_HOST = 'localhost'
_PORT = '8188'

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

class gRPCServicerImpl(grpchello_pb2_grpc.gRPCServicer):

    def SayHello(self, request, context):
        print ("called with " + request.name)
        return grpchello_pb2.HelloReply(message='Hello, %s!' % request.name)


def serve():
  server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
  grpchello_pb2_grpc.add_gRPCServicer_to_server(gRPCServicerImpl(), server)
  server.add_insecure_port('[::]:'+_PORT)
  server.start()
  try:
    while True:
      time.sleep(_ONE_DAY_IN_SECONDS)
  except KeyboardInterrupt:
    server.stop(0)

if __name__ == '__main__':
    serve()

这里包括2个实现:
- 1、在grpc的API的实现(服务实现类)gRPCServicerImpl中,实现SayHello方法。
- 2、然后,定义网络服务和端口,把grpc的API注册到网络服务的处理上。这里简单利用了grpc.server类。

使用客户端client

在客户端,调用grpc的服务API。

# -*- coding: utf-8 -*-
"""The Python implementation of the gRPC client."""
from __future__ import print_function
import grpc
from grpchello_pb2  import *    ## or import grpchello_pb2
from grpchello_pb2_grpc import *
## No grpcDemo!  from grpcDemo import grpchello_pb2, grpchello_pb2_grpc #error!

_PORT = '8188'

def run():
    conn = grpc.insecure_channel(_HOST + ':' + _PORT)
    client = gRPCStub(channel=conn)
    response = client.SayHello(HelloRequest(name='David'))
    print("received: " + response.message)


## 
if __name__ == '__main__':

    if len(sys.argv)== 2:
        print (sys.argv[1])
        _HOST = sys.argv[1]
    else:
        _HOST = 'localhost'

    #    
    run()

说明:
- 1、 def insecure_channel(target, options=None):
– Creates an insecure Channel to a server.
- 2、 客户端使用服务的Stub,调用API。

测试

分别启动服务,然后再启动客户端,可以看到调用结果。
也可以启动java、c#版本的grpc服务端、客户端,都能调用成功。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值