1

我对使用远程第三方库有点困惑:

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)

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

注册器组件:

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'

请帮助我理解这个错误以及我在哪里遗漏了什么?与第三方远程合作时,处理此类事情的最佳方法是什么?提前致谢!

4

1 回答 1

2

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

现在,您的实际问题由三个部分组成(尝试使您未来的问题更多地涉及一个问题,这使得回答它们变得更加简单)

要回答您问题的第一部分:

正如您自己已经发现的那样,您不能假设所有类型都可以通过网络传输并神奇地在另一端工作。特别是pickle只有在定义类型的模块在双方都可用时才有效。在您的情况下,该psutil模块似乎在您的客户端不可用,因此当它尝试反序列化由psutil.cpu_times().

解决方案也可以安装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())

最后,

解决您的问题“在远程与第三方合作时处理此类事情的最佳方法是什么”:

  • 尽量不要暴露你正在使用的 API 的细节和类型,尽可能将其减少为内置类型。 http://pyro4.readthedocs.io/en/latest/tipstricks.html#consider-using-basic-data-types-instead-of-custom-classes 如果你这样做正确,你也可以简单地使用 Pyro 的默认值序列化程序(蛇)而不是泡菜,并且没有任何安全问题。(这确实需要一个不依赖于火焰的解决方案,请参阅下一个项目符号)
  • 不要使用 Flame,除非您完全确定您可以接受它的安全隐患并且您无法以其他方式解决您的问题。而是使用您自己编写的专用自定义 API 创建一个普通的 Pyro 服务器类。
  • 仅公开您实际需要远程访问的远程方法。
于 2017-11-27T00:38:09.910 回答