如何使用Connections在不同的过程中调用函数

时间:2019-02-25 08:43:51

标签: python python-multiprocessing

我想使用Python实现某种形式的客户端-服务器连接,并且对于多处理是相当新的。基本上,我有一个类'Manager',它继承自multiprocessing.Process并管理从客户端到不同数据源的连接。此过程具有一些函数,例如“ get_value(key)”,应返回键数据源的值。现在,因为我希望它异步运行,所以我不能简单地从客户端进程中调用此函数。

到目前为止,我的想法是我使用管道连接客户端和管理器进程,然后将消息从客户端发送到管理器以执行此功能。我将通过管道发送一个列表来实现这一点,其中第一个元素是函数的名称,其余元素是实际函数的参数,例如['get_value','datasource1']。然后,该过程将收到此消息,并通过管道将返回值发送给客户端。看起来像这样:

from multiprocessing import Process, Pipe
import time

class Manager(Process):
    def __init__(self, connection):
        super(Process, self).__init__()
        self.connection = connection

    def run(self):
        while True:
            if self.connection.poll():
                msg = self.connection.recv()
                self.call_function(msg[0], msg[:])

    def call_function(self, name, *args):
        print('Function Called with %s' % name)
        return_val = getattr(self, name)(*args)
        self.connection.send(return_val)

    def get_value(self, key):
        return 1.0

虽然我猜这可行,但是我对这种解决方案并不满意。尤其是按字符串调用函数的方法似乎很容易出错。有没有更优雅的方法来请求在Python中执行功能?

1 个答案:

答案 0 :(得分:0)

总的来说,我认为您的方法是一种好方法(当然,还有其他方法可以完成相同的事情,但是您的一般方法没有错)。

也就是说,我将略微更改设计以添加一个“路由”组件:考虑一些逻辑,该逻辑以某种方式限制了客户端可以发送的“命令”,并且在命令和“处理程序”之间进行了钩子-即处理他们。基本上考虑一下Web框架路由(如果您熟悉该概念)。

从设计的灵活性,错误检测和安全性方面来说,这都是一个好主意(例如,您不希望客户端在您的['__del__']上致电Manager

在最基本的形式上,router可以是将命令映射到类方法的字典:

class Manager(Process):
    def __init__(self, connection):
      super(Process, self).__init__()
      self.connection = connection

      self.routes = {'do_action': self._do_action,
                     'do_other_action': some_callable,
                     'ping': lambda args: args}  # <- as long as it's callable and has the right signature...

  def call_function(self, name, *args):
      try:
          handler = self.routes[name]
      except KeyError:
          return self._error_reply('{} is not a valid command'.format(name))

      try:
          return_val = handler(*args)  # handler functions will need to throw something if arguments are wrong...
      except ValueError as e:
          return self._error_reply('Invalid command arguments: {}'.format(str(e)))
      except Exception as e:
          # This is your catch-all "internal server error" handler
          return self._error_reply(str(e))

      self.connection.send(return_val)

这当然只是方法的一个例子。您将需要以适合您的任何方式实施_error_reply()

您可以通过创建Router类并将其作为依赖项传递给Manager来对其进行扩展,从而使其更加灵活。您可能还需要考虑使Manager成为独立的事物,而不是Process的子类(因为您可能想运行它,而不管它是否在子过程中(例如在测试中))。

顺便说一句,有一些框架可以实现具有不同程度的复杂性和灵活性(Thrift,ZeroMQ等)的框架,但是如果您想做简单而又 learn 的事情,那就自己做吧我认为这是一个不错的选择。