【问题标题】:Gevent monkeypatching breaking multiprocessingGevent 猴子补丁破坏多处理
【发布时间】:2012-01-30 11:42:54
【问题描述】:

我正在尝试使用多处理的池来运行一组进程,每个进程都将运行一个 gevent greenlets 池。这样做的原因是有很多网络活动,但也有很多 CPU 活动,所以为了最大化我的带宽和我所有的 CPU 内核,我需要多个进程和 gevent 的异步猴子补丁。我正在使用多处理的管理器创建一个队列,进程将访问该队列以获取要处理的数据。

这是代码的简化片段:

import multiprocessing

from gevent import monkey
monkey.patch_all(thread=False)

manager = multiprocessing.Manager()
q = manager.Queue()

这是它产生的异常:

Traceback (most recent call last):
  File "multimonkeytest.py", line 7, in <module>
    q = manager.Queue()
  File "/usr/local/Cellar/python/2.7.2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/managers.py", line 667, in temp
    token, exp = self._create(typeid, *args, **kwds)
  File "/usr/local/Cellar/python/2.7.2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/managers.py", line 565, in _create
    conn = self._Client(self._address, authkey=self._authkey)
  File "/usr/local/Cellar/python/2.7.2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/connection.py", line 175, in Client
    answer_challenge(c, authkey)
  File "/usr/local/Cellar/python/2.7.2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/connection.py", line 409, in answer_challenge
    message = connection.recv_bytes(256)         # reject large message
 IOError: [Errno 35] Resource temporarily unavailable

我相信这一定是由于普通 socket 模块和 gevent 的 socket 模块的行为之间存在一些差异。

如果我在子进程中进行monkeypatch,则队列创建成功,但是当子进程尝试从队列中获取()时,会发生非常相似的异常。由于在子进程中执行大量网络请求,因此确实需要对套接字进行修补。

我的 gevent 版本,我认为是最新的:

>>> gevent.version_info
(1, 0, 0, 'alpha', 3)

有什么想法吗?

【问题讨论】:

标签: python multiprocessing gevent


【解决方案1】:

您提供的代码适用于我在 Windows 7 上。

编辑:

删除了之前的答案,因为我已经在 Ubuntu 11.10 VPS 上尝试过您的代码,并且遇到了同样的错误。

看起来像Eventlet have this issue too

【讨论】:

  • 在没有实际执行任何请求的情况下发生错误,但我也使用本地 gevent bottle.py 供电的服务器进行了测试。我正在使用 Python 2.7 获得 OS X Lion 和 Ubuntu 11.04 VPS 的回溯。你使用的是什么操作系统/Python/Gevent?
  • @user964375,嗯,在 Ubuntu 11.10 (VPS) 上我遇到了同样的错误。 Win7 上没有错误。
  • 在 windows xp 和代码上测试过,即使没有 gevent 部分在一段时间后中断。似乎是多处理模块中的一个问题。
【解决方案2】:

使用monkey.patch_all(thread=False, socket=False)

我在类似的情况下遇到了同样的问题,并在 gevent/monkey.pypatch_socket() 函数下跟踪到第 115 行:_socket.socket = socket.socket。将此行注释掉可以防止损坏。

这是 gevent 用它自己的替换 stdlib socket 库的地方。 multiprocessing.connection 相当广泛地使用 socket 库,显然不能容忍这种变化。

具体来说,在您导入的模块执行gevent.monkey.patch_all() 调用而不设置socket=False 的任何情况下,您都会看到这一点。在我的例子中,是 grequests 做到了这一点,我不得不重写套接字模块的补丁来修复这个错误。

【讨论】:

  • 我可以在不修补套接字的情况下将 requests 库与 gevent 一起使用吗?
【解决方案3】:

不幸的是,众所周知,在 gevent 上下文中应用多处理会引发问题。但是,您的理由是合理的(“大量网络活动,但也有大量 CPU 活动”)。如果您愿意,请查看http://gehrcke.de/gipc。这主要是为您的用例设计的。使用 gipc,您可以轻松地生成一些完全感知 gevent 的子进程,并让它们通过管道相互协作和/或与父进程协作。

如果您有具体问题,欢迎回复我。

【讨论】:

  • 我个人认为问题出在粗心的猴子补丁上。任何使用 gevent 的东西都应该带有一个非常大的警告标志,说明它会破坏什么。尚未遇到这种 gevent 多处理不兼容的特殊怪癖的普通程序员会(正确地)假设 gevent 不会破坏 stdlib 功能。 gevent 在修补套接字时不会修补 IPC 功能(这是可行的),这并不是很明显。
  • 我尝试了 gipc 方法。由于它错过了 Queue 对象,因此使用管道的替代实现过于复杂。请参阅nose-gevented-multiprocess 模块
【解决方案4】:

如果您将使用原始队列,那么即使使用猴子补丁套接字,您的代码也将正常工作。

import multiprocessing

from gevent import monkey
monkey.patch_all(thread=False)

q= multiprocessing.Queue()

【讨论】:

    【解决方案5】:

    编写了一个替代的 Nose Multiprocess 插件 - 这个插件应该可以很好地与各种基于 Gevent 的疯狂补丁配合使用。

    https://pypi.python.org/pypi/nose-gevented-multiprocess/

    https://github.com/dvdotsenko/nose_gevent_multiprocess

    • 工作进程从multiprocess.fork 切换到普通subprocess.popen(为我修复了模块级错误共享对象问题)
    • 从 multiprocess.Queue 切换到 JSON-RPC over HTTP 用于主到客户端 RPC
    • 现在理论上可以将测试分布到多台机器上

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-04
      • 2017-01-25
      • 2012-07-14
      • 1970-01-01
      • 1970-01-01
      • 2012-06-14
      • 1970-01-01
      • 2016-09-01
      相关资源
      最近更新 更多