如何在Pyro v.4.63中正确使用第三方库

时间:2017-11-25 12:33:31

标签: python pyro pyro4

我对使用远程第三方库感到困惑:

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()

CPU组件是这样的:

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)

如果我需要远程模块,但在本地没有它,为什么我不能这样做:

Registrator组件:

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 个答案:

答案 0 :(得分:2)

首先,您似乎正在使用&#39;火焰&#39; Pyro4的功能,需要pickle序列化协议。这很强大,但并非没有问题。 你确定你想要/需要使用火焰吗?我希望你知道它的安全隐患。 http://pyro4.readthedocs.io/en/latest/flame.html

现在,您的实际问题包含三个部分(尝试让您的未来问题更多地仅针对一个问题,这使得回答它们变得更加简单)

回答问题的第一部分:

正如您已经发现的那样,您不能假设所有类型都可以通过网络传输,并且神奇地只是在另一侧工作。特别是对于pickle,这仅适用于其中的模块 类型是定义的,双方都可以使用。 在您的情况下,似乎psutil模块在​​您的客户端不可用,因此当它尝试反序列化psutil.cpu_times()返回的对象时出现pickle错误。

解决方案可能是在您的客户端安装psutil。 您还发现了另一种解决方案:不要通过线路传递自定义类型的对象,但尽可能坚持内置类型。将结果转换为元组或列表或字典时, 这些可以序列化和反序列化而不会出现任何问题(如果它们包含的所有对象都是内置类型的话)。

回答问题的第二部分:

此代码看起来像您在第一个问题中尝试找到问题的解决方法? 无论如何,错误应该是明确的;它意味着它所说的:您的代理连接到的远程对象,并没有暴露您尝试呼叫的方法。您应该添加@Pyro4.expose装饰 正确揭露它。

那就是说,你已经使用了Flame,那你为什么不使用它的远程模块功能呢?见http://pyro4.readthedocs.io/en/latest/flame.html#flame-object-and-examples

例如(我假设您在客户端安装了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())

最后,

解决您的问题&#34;在远程使用第三方时处理此类事情的最佳方法是什么?

  • 尽量不公开您正在使用的API的详细信息和类型,尽可能地将其减少为内置类型。 http://pyro4.readthedocs.io/en/latest/tipstricks.html#consider-using-basic-data-types-instead-of-custom-classes 如果您正确地执行此操作,您也可以简单地使用Pyro的默认序列化程序(serpent)而不是pickle,并且没有任何安全问题。 (这需要一个不依赖火焰的解决方案,见下一个项目符号)
  • 不要使用Flame,除非您完全确定自己对安全的影响是否正常,并且无法以其他方式解决您的问题。而是使用您自己编写的专用自定义API创建一个普通的Pyro服务器类。
  • 仅公开您实际需要远程访问的远程方法。