python如何通过protobuf实现rpc


Posted in Python onMarch 06, 2016

由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc。rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行google,这里只是做个简单的介绍。rpc的主要功能是让分布式系统的实现更为简单,为提供强大的远程调用而不损失本地调用语义的简洁性。为了实现这个目标,rpc框架需要提供一种透明调用机制让使用者不必显示区分本地调用还是远程调用。rpc架构涉及的组件如下:

python如何通过protobuf实现rpc

客户方像调用本地方法一样去调用远程接口方法,RPC 框架提供接口的代理实现,实际的调用将委托给代理RpcProxy 。代理封装调用信息并将调用转交给RpcInvoker 去实际执行。在客户端的RpcInvoker 通过连接器RpcConnector 去维持与服务端的通道RpcChannel,并使用RpcProtocol 执行协议编码(encode)并将编码后的请求消息通过通道发送给服务方。RPC 服务端接收器 RpcAcceptor 接收客户端的调用请求,同样使用RpcProtocol 执行协议解码(decode)。解码后的调用信息传递给RpcProcessor 去控制处理调用过程,最后再委托调用给RpcInvoker 去实际执行并返回调用结果。

protobuf rpc在上面组件中主要扮演RpcProtocol的角色,使得我们省去了协议的设计,并且protobuf协议在编码和空间效率都是上非常高效的,这也是很多公司采用protobuf作为数据序列化和通信协议的原因。同时protobuf rpc定义了一个抽象的rpc框架,如下图所示:

python如何通过protobuf实现rpc

RpcServiceStub和RpcService类是protobuf编译器根据proto定义生成的类,RpcService定义了服务端暴露给客户端的函数接口,具体实现需要用户自己继承这个类来实现。RpcServiceStub定义了服务端暴露函数的描述,并将客户端对RpcServiceStub中函数的调用统一转换到调用RpcChannel中的CallMethod方法,CallMethod通过RpcServiceStub传过来的函数描述符和函数参数对该次rpc调用进行encode,最终通过RpcConnecor发送给服务方。对方以客户端相反的过程最终调用RpcSerivice中定义的函数。事实上,protobuf rpc的框架只是RpcChannel中定义了空的CallMethod,所以具体怎样进行encode和调用RpcConnector都要自己实现。RpcConnector在protobuf中没有定义,所以这个完成由用户自己实现,它的作用就是收发rpc消息包。在服务端,RpcChannel通过调用RpcService中的CallMethod来具体调用RpcService中暴露给客户端的函数。

介绍了这么多,对于怎么样用protobuf rpc来实现一个rpc肯定还是一头雾水吧,下面就用protobuf rpc来实现一个简单的python版rpc demo吧。

下面直接给出demo描述PRC的proto文件,至于proto文件的编写规则可以参考protobuf官网。

common.proto文件:

package game;

message RequestMessage
{
  required string message = 1;
}

message ResponseMessage
{
  required string message = 1;
}

game_service.proto文件:

package game;

import "common.proto";
option py_generic_services = true;

service GameService
{
  rpc connect_server(RequestMessage) returns(RequestMessage);
}

common.proto文件描述了RPC中收发的消息;game_service.proto描述了服务器导出的connect_server函数,该函数接受RequestMessage对象作为参数,并返回RequestMessage对象。在使用PRC协议时,必须加上option py_generic_services  = true;可选项,要不然编译器不会生成包含connect_server函数的GameService描述。

使用编译器protoc编译proto文件,具体命令为:
protoc.exe --python_out=. game_service.proto
编译后生成的文件为game_service_pb2.py,该文件主要是实现了GameService和GameService_Stub类。GameService_Stub类用于客户端调用者来调用GameService的服务。
前面已经说了,在客户端,RpcChannel只实现了一个空的CallMethod,所以需要继承RpcChannel重新这个函数来encode消息和发送消息。在服务端RpcChannel需要调用CallMethod来调用Service中的函数。具体实现如下:

class MyRpcChannel(service.RpcChannel):
  def __init__(self, rpc_service, conn):
    super(MyRpcChannel, self).__init__()
    self.logger = LogManager.get_logger("MyRpcChannel")

  def CallMethod(self, method_descriptor, rpc_controller, request, response_class, done):
    """"protol buffer rpc 需要的函数,用来发送rpc调用"""
    self.logger.info('CallMethod')
    cmd_index = method_descriptor.index
    assert(cmd_index < 65535)
    data = request.SerializeToString()
    total_len = len(data) + 2
    self.conn.send_data(''.join([pack('<I', total_len), pack('<H', cmd_index), data]))

  def from_request(self):
    """"从网络解析出一个完整的请求之后调的函数"""
    index_data = self.rpc_request.data[0:2]    
    cmd_index = unpack('<H', index_data)[0]  
    rpc_service = self.rpc_service
    s_descriptor = rpc_service.GetDescriptor()
    method = s_descriptor.methods[cmd_index]  
    try:
      request = rpc_service.GetRequestClass(method)()
      serialized = self.rpc_request.data[2:]    
      request.ParseFromString(serialized)  
      rpc_service.CallMethod(method, self.controller, request, None)
    except:
      self.logger.error("Call rpc method failed!")
      self.logger.log_last_except()
    return True

最后就是继承GameService,并实现connect_server函数了。

class GameService(game_service_pb2.GameService):
  def __init__(self):
    self.logger = LogManager.get_logger("GameService")

  def connect_server(self, rpc_controller, request, callback):
    self.logger.info('%s', request.message)

 至于用于网络收发消息的RpcConnector,可以使用python的asyncore库实现,具体实现在这就不讨论了。

从上面的实现来看,protobuf rpc的实现主要包括编写proto文件并编译生成对应的service_pb2文件,继承RpcChannel并实现CallMethod和调用Service的CallMethod,继承Service来实现暴露给客户端的函数。

以上就是本文的全部内容,希望对大家的学习有所帮助。

Python 相关文章推荐
Python中os和shutil模块实用方法集锦
May 13 Python
Python XML RPC服务器端和客户端实例
Nov 22 Python
python开发中module模块用法实例分析
Nov 12 Python
Python正则表达式常用函数总结
Jun 24 Python
浅析Python pandas模块输出每行中间省略号问题
Jul 03 Python
Python实现手写一个类似django的web框架示例
Jul 20 Python
tensorflow tf.train.batch之数据批量读取方式
Jan 20 Python
Python中import导入不同目录的模块方法详解
Feb 18 Python
Python configparser模块配置文件过程解析
Mar 03 Python
Scrapy框架介绍之Puppeteer渲染的使用
Jun 19 Python
使用scrapy ImagesPipeline爬取图片资源的示例代码
Sep 28 Python
pycharm安装深度学习pytorch的d2l包失败问题解决
Mar 25 Python
使用Python保存网页上的图片或者保存页面为截图
Mar 05 #Python
Python发送form-data请求及拼接form-data内容的方法
Mar 05 #Python
Python多线程爬虫简单示例
Mar 04 #Python
使用Python来开发Markdown脚本扩展的实例分享
Mar 04 #Python
使用py2exe在Windows下将Python程序转为exe文件
Mar 04 #Python
用Python编写简单的微博爬虫
Mar 04 #Python
python相似模块用例
Mar 04 #Python
You might like
PHP抓取淘宝商品的用户晒单评论+图片+搜索商品列表实例
2016/04/14 PHP
改善你的jQuery的25个步骤 千倍级效率提升
2010/02/11 Javascript
基于jquery ajax 用户无刷新登录方法详解
2012/04/28 Javascript
首页图片漂浮效果示例代码
2014/06/05 Javascript
node.js中的url.parse方法使用说明
2014/12/10 Javascript
jquery中EasyUI使用技巧小结
2015/02/10 Javascript
Javascript中作用域的详细介绍
2016/10/06 Javascript
浅谈jquery采用attr修改form表单enctype不起作用的问题
2016/11/25 Javascript
JavaScript仿支付宝6位数字密码输入框
2016/12/29 Javascript
微信小程序选择图片和放大预览图片功能
2017/11/02 Javascript
nodejs基于express实现文件上传的方法
2018/03/19 NodeJs
vue-cli脚手架-bulid下的配置文件
2018/03/27 Javascript
jQuery实现点击旋转,再点击恢复初始状态动画效果示例
2018/12/11 jQuery
es6中使用map简化复杂条件判断操作实例详解
2020/02/19 Javascript
Jquery滑动门/tab切换实现方法完整示例
2020/06/05 jQuery
JS实现audio音频剪裁剪切复制播放与上传(步骤详解)
2020/07/28 Javascript
[01:01]青春无憾,一战成名——DOTA2全国高校联赛开启
2018/02/25 DOTA
[56:00]2018DOTA2亚洲邀请赛 4.6 淘汰赛 VP vs TNC 第二场
2018/04/10 DOTA
浅谈Scrapy框架普通反爬虫机制的应对策略
2017/12/28 Python
基于Python实现扑克牌面试题
2019/12/11 Python
python递归函数求n的阶乘,优缺点及递归次数设置方式
2020/04/02 Python
新手学python应该下哪个版本
2020/06/11 Python
加拿大当代时尚服饰、配饰和鞋类专业零售商和制造商:LE CHÂTEAU
2017/10/06 全球购物
瑞典最大的儿童用品网上商店:pinkorblue.se
2021/03/09 全球购物
NULL是什么,它是怎么定义的
2015/05/09 面试题
个人思想理论学习的自我鉴定
2013/11/30 职场文书
《一株紫丁香》教学反思
2014/02/19 职场文书
售后服务经理岗位职责
2014/02/25 职场文书
同学聚会主持词
2014/03/18 职场文书
网站美工岗位职责
2014/04/02 职场文书
艺人经纪人岗位职责
2014/04/15 职场文书
论文评语大全
2014/04/29 职场文书
讲解员培训方案
2014/05/04 职场文书
2014年新农村建设工作总结
2014/12/01 职场文书
学生退学证明
2015/06/23 职场文书
Python包argparse模块常用方法
2021/06/04 Python