Как правильно работать со сторонними библиотеками в Pyro v.4.63

Я немного запутался в работе с удаленными сторонними библиотеками:

1) Например, у меня есть код сервера:

import Pyro4
import Pyro4.naming
import Pyro4.utils.flame
Pyro4.config.REQUIRE_EXPOSE = False
Pyro4.config.FLAME_ENABLED = True
Pyro4.config.SERIALIZERS_ACCEPTED = set(["pickle"])
Pyro4.config.SERIALIZER = 'pickle'  # for flameserver


def _main():
    """Start RPC server."""
    ip_address = Pyro4.socketutil.getIpAddress(None, workaround127=True)
    ns_daemon = Pyro4.naming.NameServerDaemon(host=ip_address, port=RPC_PORT)
    Pyro4.utils.flame.start(ns_daemon)

    from remote_management.rpc.component.shell import Shell
    shell_uri = ns_daemon.register(Shell())
    ns_daemon.nameserver.register("shell", shell_uri)
    from remote_management.rpc.component.cpu import CPU
    cpu_uri = ns_daemon.register(CPU())
    ns_daemon.nameserver.register("cpu", cpu_uri)

    ns_daemon.requestLoop()
    ns_daemon.close()

if __name__ == "__main__":
    _main()

Компонент процессора выглядит так:

import psutil

class CPU(object):
    def ps(self):
        return psutil.cpu_times()

Когда я использую это в клиенте, как это:

import Pyro4
import Pyro4.utils.flame
import Pyro4.errors

Pyro4.config.SERIALIZER = "pickle"

proxy = Pyro4.Proxy("PYRONAME:cpu@myhost:47976")
print proxy.ps()

Я получаю ошибку:

Traceback (most recent call last):
  File "t.py", line 145, in <module>
    print proxy.ps()
  File "/home/korolev/.envs/auto/lib/python2.7/site-packages/Pyro4/core.py", line 187, in __call__
    return self.__send(self.__name, args, kwargs)
  File "/home/korolev/.envs/auto/lib/python2.7/site-packages/Pyro4/core.py", line 464, in _pyroInvoke
    data = serializer.deserializeData(msg.data, compressed=msg.flags & message.FLAGS_COMPRESSED)
  File "/home/korolev/.envs/auto/lib/python2.7/site-packages/Pyro4/util.py", line 170, in deserializeData
    return self.loads(data)
  File "/home/korolev/.envs/auto/lib/python2.7/site-packages/Pyro4/util.py", line 451, in loads
        return pickle.loads(data)

ImportError: No module named psutil._pslinux

Как видите, его нельзя десериализовать. И если я изменюсь, вернитесь к:

return tuple(psutil.cpu_times())тогда это работает.

2) Я думаю, что эта следующая проблема имеет такую ​​же природу: код сервера почти такой же, за исключением компонента

from remote_management.rpc.component.registrator import ModuleRegistrator
reg_uri = ns_daemon.register(ModuleRegistrator())
ns_daemon.nameserver.register("module_registrator", reg_uri)

Если мне нужен удаленный модуль, но нет его локально, почему я не могу сделать так:

Компонент регистратора:

class ModuleRegistrator(object):
    def register(self, module):
        module = importlib.import_module(module)
        self._pyroDaemon.register(module)
        return module

поэтому, когда я использую его в клиенте:

proxy = Pyro4.Proxy("PYRONAME:module_registrator@myhost:47976")
print proxy.register("psutil").cpu_times()

Я получаю ошибку:

Traceback (most recent call last):
  File "t.py", line 145, in <module>
    print proxy.register('psutil').cpu_times()
  File "/home/korolev/.envs/auto/lib/python2.7/site-packages/Pyro4/core.py", line 279, in __getattr__
    raise AttributeError("remote object '%s' has no exposed attribute or method '%s'" % (self._pyroUri, name))
AttributeError: remote object 'PYRONAME:module_registrator@myhost:47976' has no exposed attribute or method 'register'

Пожалуйста, помогите мне разобраться в этих ошибках и где я что-то упускаю? И как лучше всего справляться с такими вещами при удаленной работе со сторонними организациями? Заранее спасибо!

1 ответ

Решение

Во-первых, вы, кажется, используете функцию "пламени" в Pyro4, которая требует pickle протокол сериализации. Это мощно, но не без проблем. Вы уверены, что хотите / должны использовать пламя? Я надеюсь, что вы знаете о его последствиях для безопасности. http://pyro4.readthedocs.io/en/latest/flame.html

Теперь ваш фактический вопрос состоит из трех частей (постарайтесь сделать ваши будущие вопросы больше просто об одной проблеме, которая делает ответы на них намного проще)

Чтобы ответить на первую часть вашего вопроса:

Как вы уже узнали сами, вы не можете предполагать, что все типы могут просто передаваться по сети и волшебным образом работать на другой стороне. Особенно с pickle это работает, только если модуль, в котором определен тип, доступен с обеих сторон. В вашем случае кажется, что psutil модуль недоступен на вашей стороне клиента, следовательно, ошибка pickle при попытке десериализации объекта, возвращаемого psutil.cpu_times(),

Решение может быть установить psutil на стороне вашего клиента, а также. Вы также обнаружили альтернативное решение: не передавайте объекты пользовательских типов по проводам, но максимально придерживайтесь встроенных типов. Когда вы конвертируете результат в кортеж, список или диктовку, их можно сериализовать и десериализовать без каких-либо проблем (если все содержащиеся в них объекты также имеют встроенный тип).

Чтобы ответить на вторую часть вашего вопроса:

Этот код выглядит так, как будто вы пытаетесь найти решение проблемы в первом вопросе? В любом случае, ошибка должна быть понятной; это означает, что в нем говорится: что удаленный объект, к которому подключен ваш прокси, не имеет метода, который вы пытаетесь вызвать. Вы должны добавить @Pyro4.expose украшение, чтобы правильно выставить его.

Тем не менее, вы уже используете Flame, так почему вы не используете его функцию удаленного модуля? См. http://pyro4.readthedocs.io/en/latest/flame.html.

Например (я полагаю, вы установили psutil на стороне клиента):

import Pyro4.utils.flame

Pyro4.config.SERIALIZER = "pickle"
flame = Pyro4.utils.flame.connect("yourhost:9999")

psutilmodule = flame.module("psutil")
print("remote cpu:", psutilmodule.cpu_times())

В заключение,

чтобы ответить на ваш вопрос "как лучше всего справляться с такими вещами при удаленной работе со сторонними организациями":

  • старайтесь не раскрывать детали и типы API, с которым вы работаете, по возможности уменьшите его до встроенных типов. http://pyro4.readthedocs.io/en/latest/tipstricks.html Если вы делаете это правильно, вы также можете просто использовать Pyro по умолчанию Сериализатор (змей) вместо рассола и не имеет никаких проблем с безопасностью. (для этого требуется решение, не зависящее от пламени, см. следующий пункт)
  • не используйте Flame, если вы абсолютно не уверены, что вы в порядке с его последствиями для безопасности и что вы не можете решить свою проблему другим способом. Вместо этого создайте обычный класс сервера Pyro с выделенным пользовательским API, который вы написали сами.
  • выставляйте только те удаленные методы, которые вам действительно нужны для удаленного доступа.
Другие вопросы по тегам