Python如何实现远程方法调用


Posted in Python onAugust 07, 2020

问题

你想在一个消息传输层如 sockets 、multiprocessing connections 或 ZeroMQ 的基础之上实现一个简单的远程过程调用(RPC)。

解决方案

将函数请求、参数和返回值使用pickle编码后,在不同的解释器直接传送pickle字节字符串,可以很容易的实现RPC。 下面是一个简单的PRC处理器,可以被整合到一个服务器中去:

# rpcserver.py

import pickle
class RPCHandler:
  def __init__(self):
    self._functions = { }

  def register_function(self, func):
    self._functions[func.__name__] = func

  def handle_connection(self, connection):
    try:
      while True:
        # Receive a message
        func_name, args, kwargs = pickle.loads(connection.recv())
        # Run the RPC and send a response
        try:
          r = self._functions[func_name](*args,**kwargs)
          connection.send(pickle.dumps(r))
        except Exception as e:
          connection.send(pickle.dumps(e))
    except EOFError:
       pass

要使用这个处理器,你需要将它加入到一个消息服务器中。你有很多种选择, 但是使用 multiprocessing 库是最简单的。下面是一个RPC服务器例子:

from multiprocessing.connection import Listener
from threading import Thread

def rpc_server(handler, address, authkey):
  sock = Listener(address, authkey=authkey)
  while True:
    client = sock.accept()
    t = Thread(target=handler.handle_connection, args=(client,))
    t.daemon = True
    t.start()

# Some remote functions
def add(x, y):
  return x + y

def sub(x, y):
  return x - y

# Register with a handler
handler = RPCHandler()
handler.register_function(add)
handler.register_function(sub)

# Run the server
rpc_server(handler, ('localhost', 17000), authkey=b'peekaboo')

为了从一个远程客户端访问服务器,你需要创建一个对应的用来传送请求的RPC代理类。例如

import pickle

class RPCProxy:
  def __init__(self, connection):
    self._connection = connection
  def __getattr__(self, name):
    def do_rpc(*args, **kwargs):
      self._connection.send(pickle.dumps((name, args, kwargs)))
      result = pickle.loads(self._connection.recv())
      if isinstance(result, Exception):
        raise result
      return result
    return do_rpc

要使用这个代理类,你需要将其包装到一个服务器的连接上面,例如:

>>> from multiprocessing.connection import Client
>>> c = Client(('localhost', 17000), authkey=b'peekaboo')
>>> proxy = RPCProxy(c)
>>> proxy.add(2, 3)

5
>>> proxy.sub(2, 3)
-1
>>> proxy.sub([1, 2], 4)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
 File "rpcserver.py", line 37, in do_rpc
  raise result
TypeError: unsupported operand type(s) for -: 'list' and 'int'
>>>

要注意的是很多消息层(比如 multiprocessing )已经使用pickle序列化了数据。 如果是这样的话,对 pickle.dumps() 和 pickle.loads() 的调用要去掉。

讨论

RPCHandler 和 RPCProxy 的基本思路是很比较简单的。 如果一个客户端想要调用一个远程函数,比如 foo(1, 2, z=3) ,代理类创建一个包含了函数名和参数的元组 ('foo', (1, 2), {'z': 3}) 。 这个元组被pickle序列化后通过网络连接发生出去。 这一步在 RPCProxy 的 __getattr__() 方法返回的 do_rpc() 闭包中完成。 服务器接收后通过pickle反序列化消息,查找函数名看看是否已经注册过,然后执行相应的函数。 执行结果(或异常)被pickle序列化后返回发送给客户端。我们的实例需要依赖 multiprocessing 进行通信。 不过,这种方式可以适用于其他任何消息系统。例如,如果你想在ZeroMQ之上实习RPC, 仅仅只需要将连接对象换成合适的ZeroMQ的socket对象即可。

由于底层需要依赖pickle,那么安全问题就需要考虑了 (因为一个聪明的黑客可以创建特定的消息,能够让任意函数通过pickle反序列化后被执行)。 因此你永远不要允许来自不信任或未认证的客户端的RPC。特别是你绝对不要允许来自Internet的任意机器的访问, 这种只能在内部被使用,位于防火墙后面并且不要对外暴露。

作为pickle的替代,你也许可以考虑使用JSON、XML或一些其他的编码格式来序列化消息。 例如,本机实例可以很容易的改写成JSON编码方案。还需要将 pickle.loads() 和 pickle.dumps() 替换成 json.loads() 和 json.dumps() 即可:

# jsonrpcserver.py
import json

class RPCHandler:
  def __init__(self):
    self._functions = { }

  def register_function(self, func):
    self._functions[func.__name__] = func

  def handle_connection(self, connection):
    try:
      while True:
        # Receive a message
        func_name, args, kwargs = json.loads(connection.recv())
        # Run the RPC and send a response
        try:
          r = self._functions[func_name](*args,**kwargs)
          connection.send(json.dumps(r))
        except Exception as e:
          connection.send(json.dumps(str(e)))
    except EOFError:
       pass

# jsonrpcclient.py
import json

class RPCProxy:
  def __init__(self, connection):
    self._connection = connection
  def __getattr__(self, name):
    def do_rpc(*args, **kwargs):
      self._connection.send(json.dumps((name, args, kwargs)))
      result = json.loads(self._connection.recv())
      return result
    return do_rpc

实现RPC的一个比较复杂的问题是如何去处理异常。至少,当方法产生异常时服务器不应该奔溃。 因此,返回给客户端的异常所代表的含义就要好好设计了。 如果你使用pickle,异常对象实例在客户端能被反序列化并抛出。如果你使用其他的协议,那得想想另外的方法了。 不过至少,你应该在响应中返回异常字符串。我们在JSON的例子中就是使用的这种方式。

以上就是Python如何实现远程方法调用的详细内容,更多关于Python远程方法调用的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
浅析Python中的序列化存储的方法
Apr 28 Python
Python读写Json涉及到中文的处理方法
Sep 12 Python
pandas获取groupby分组里最大值所在的行方法
Apr 20 Python
安装docker-compose的两种最简方法
Jul 30 Python
opencv设置采集视频分辨率方式
Dec 10 Python
Django添加bootstrap框架时无法加载静态文件的解决方式
Mar 27 Python
Python实现RabbitMQ6种消息模型的示例代码
Mar 30 Python
keras中的卷积层&amp;池化层的用法
May 22 Python
Python 字典中的所有方法及用法
Jun 10 Python
python能做哪方面的工作
Jun 15 Python
python爬取抖音视频的实例分析
Jan 19 Python
解决IDEA翻译插件Translation报错更新TTK失败不能使用
Apr 24 Python
记一次django内存异常排查及解决方法
Aug 07 #Python
python正则表达式 匹配反斜杠的操作方法
Aug 07 #Python
Pygame框架实现飞机大战
Aug 07 #Python
python爬取网易云音乐热歌榜实例代码
Aug 07 #Python
Python变量格式化输出实现原理解析
Aug 06 #Python
Python实现Canny及Hough算法代码实例解析
Aug 06 #Python
vscode调试django项目的方法
Aug 06 #Python
You might like
php中json_encode处理gbk与gb2312中文乱码问题的解决方法
2014/07/10 PHP
WIN8.1下搭建PHP5.6环境
2015/04/29 PHP
Yii开启片段缓存的方法
2016/03/28 PHP
对laravel的session获取与存取方法详解
2019/10/08 PHP
写的htc的数据表格
2007/01/20 Javascript
ExtJS 2.0实用简明教程 之ExtJS版的Hello
2009/04/29 Javascript
jQuery学习笔记之Helloworld
2010/12/22 Javascript
JS判断对象是否存在的10种方法总结
2013/12/23 Javascript
jQuery简单图表peity.js使用示例
2014/05/02 Javascript
Jquery 返回json数据在IE浏览器中提示下载的问题
2014/05/18 Javascript
javascript使用appendChild追加节点实例
2015/01/12 Javascript
JS+CSS实现六级网站导航主菜单效果
2015/09/28 Javascript
AngularJS入门教程之AngularJS模型
2016/04/18 Javascript
JavaScript判断是否是微信浏览器
2016/06/13 Javascript
vue中计算属性(computed)、methods和watched之间的区别
2017/07/27 Javascript
Vue.js递归组件构建树形菜单
2017/12/24 Javascript
js 图片转base64的方式(两种)
2018/04/24 Javascript
nodejs中使用worker_threads来创建新的线程的方法
2021/01/22 NodeJs
[01:07:22]2014 DOTA2华西杯精英邀请赛 5 24 DK VS VG加赛
2014/05/26 DOTA
Python中利用原始套接字进行网络编程的示例
2015/05/04 Python
Python3 模块、包调用&amp;路径详解
2017/10/25 Python
matplotlib subplots 设置总图的标题方法
2018/05/25 Python
用pycharm开发django项目示例代码
2019/06/13 Python
python中用logging实现日志滚动和过期日志删除功能
2019/08/20 Python
在django中使用post方法时,需要增加csrftoken的例子
2020/03/13 Python
CSS3常用的几种颜色渐变模式总结
2016/11/18 HTML / CSS
css3编写浏览器背景渐变背景色的方法
2018/03/05 HTML / CSS
便携式太阳能系统的创新者:GOAL ZERO
2018/02/04 全球购物
荷兰音乐会和音乐剧门票订购网站:Topticketshop
2019/08/27 全球购物
澳大利亚二手奢侈品网站:Modsie
2019/09/23 全球购物
平面网站制作专科生的自我评价分享
2013/12/11 职场文书
公司人力资源的自我评价
2014/01/02 职场文书
《世界多美呀》教学反思
2014/03/02 职场文书
党员个人剖析材料2014
2014/10/08 职场文书
加强作风建设心得体会
2014/10/22 职场文书
搞笑婚庆主持词
2015/06/29 职场文书