Как правильно работать со сторонними библиотеками в 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, который вы написали сами.
- выставляйте только те удаленные методы, которые вам действительно нужны для удаленного доступа.