Как вызвать функцию в другом процессе, используя Соединения - PullRequest
0 голосов
/ 25 февраля 2019

Я хочу реализовать своего рода соединение клиент-сервер с использованием 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

Хотя я предполагаю, что это сработает, я не очень доволен этим решением.Особенно метод call-function-by-string кажется очень подверженным ошибкам.Есть ли более элегантный способ запроса выполнения функции в Python?

1 Ответ

0 голосов
/ 25 февраля 2019

Я думаю, что ваш подход, в целом, хороший (конечно, есть и другие способы сделать то же самое, но в вашем общем подходе нет ничего плохого).

Тем не менее, я бы немного изменил дизайн, добавив компонент «маршрутизации»: подумайте о некоторой логике, которая каким-то образом ограничивает то, какие «команды» могут отправлять клиенты, и перехватывает между командами и «обработчиками» - чтоэто функции, которые их обрабатывают.В основном подумайте о маршрутизации в Web Framework (если вы знакомы с концепцией).

Это хорошая идея как с точки зрения гибкости дизайна, так и с точки зрения обнаружения ошибок и с точки зрения безопасности (вы не хотите, чтобы клиенты вызывали ['__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, что сделает его еще более гибким. Возможно, вы захотите подуматьо том, чтобы сделать ваш менеджер отдельным, а не подклассом Process (потому что вы можете запускать его независимо от того, находится ли он в подпроцессе - например, в тестировании).

Кстати, существуют платформы дляреализовать такие вещи с различной степенью сложности и гибкости (Thrift, ZeroMQ, ...), но если вы хотите сделать что-то простое и выучить , сделать это самостоятельномое мнение отличный выбор.

...