关于python:CherryPy,Multiprocessing和Gevent monkey补丁

CherryPy, Multiprocessing and Gevent monkey patching

我正在尝试结合使用cherrypy+多处理(启动工人的"进程")+gevent(从工人的"进程"中启动并行I/O greenlet)。这看起来最简单的方法是monkeypatch多处理,因为greenlets只能在主应用程序进程中操作。

但是,看起来猴子修补对多处理的某些部分有效,而对其他部分无效。这是我的Cherrypy服务器示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from gevent import monkey
monkey.patch_all()

import gevent
import cherrypy
import multiprocessing

def launch_testfuncs():
    jobs = [gevent.spawn(testfunc)
            for i in range(0, 12)]

    gevent.joinall(jobs, timeout=10)

def testfunc():
    print 'testing'

class HelloWorld(object):
    def index(self):
        launch_testfuncs()

        return"Hello World!"
    index.exposed = True

    def index_proc(self):
        proc = multiprocessing.Process(target=launch_testfuncs)
        proc.start()
        proc.join()

        return"Hello World 2!"
    index_proc.exposed = True

    def index_pool(self):
        pool = multiprocessing.Pool(1)
        return"Hello World 3!"
    index_pool.exposed = True

    def index_namespace(self):
        manager = multiprocessing.Manager()
        anamespace = manager.Namespace()
        anamespace.val = 23
        return"Hello World 4!"
    index_namespace.exposed = True


cherrypy.quickstart(HelloWorld())

猴子修补后的工作如下:

  • index—直接从樱桃类中产卵。
  • index_proc—使用multiprocessing.Process启动一个新的进程,然后从该进程中产生绿叶

以下内容存在问题:

  • index_pool发射multiprocessing.Pool挂起,永不返回
  • index_namespace—初始化multiprocessing.Manager名称空间以管理池/工人集合中的共享内存—返回以下错误消息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    [15/Nov/2012:17:19:31] HTTP Traceback (most recent call last):
      File"/Library/Python/2.7/site-packages/cherrypy/_cprequest.py", line 656, in respond
    response.body = self.handler()
      File"/Library/Python/2.7/site-packages/cherrypy/lib/encoding.py", line 188, in __call__
    self.body = self.oldhandler(*args, **kwargs)
      File"/Library/Python/2.7/site-packages/cherrypy/_cpdispatch.py", line 34, in __call__
    return self.callable(*self.args, **self.kwargs)
      File"server.py", line 39, in index_namespace
    anamespace = manager.Namespace()
      File"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/managers.py", line 667, in temp
    token, exp = self._create(typeid, *args, **kwds)
      File"/System/Library/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"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/connection.py", line 175, in Client
    answer_challenge(c, authkey)
      File"/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/connection.py", line 414, in answer_challenge
    response = connection.recv_bytes(256)        # reject large message
    IOError: [Errno 35] Resource temporarily unavailable

我试图在Gevent文档中找到一些与此相关的文档,但找不到任何与此相关的文档。只是Gevent的猴子修补不完整吗?有没有其他人也有类似的问题,有没有解决的办法?


我也遇到过和你一样的问题。我的解决方案是,我刚刚使用multiprocessing.process()来生成固定数目的进程。最后加入所有人,等待完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python
# encoding: utf-8

from gevent import monkey
monkey.patch_all()

import gevent
import multiprocessing as mp


NUM = 10


def work(i):
    jobs = [gevent.spawn(func, i)
            for i in range(0, 12)]
    gevent.joinall(jobs)
    print"{} Done {}".format(mp.current_process().name, i)


def func(x):
    print"Gevent: {}".format(x)

def main():

    processes = [mp.Process(name="Process-{}".format(i), target=work, args=(i,)) for i in xrange(NUM)]

    for process in processes:
        process.start()

    for process in processes:
        process.join()


if __name__ == '__main__':
    main()

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Gevent: 0
Gevent: 1
Gevent: 2
Gevent: 3
Gevent: 4
Gevent: 5
Gevent: 6
Gevent: 7
Gevent: 8
Gevent: 9
Gevent: 10
Gevent: 11
Process-0 Done 11
Gevent: 0
Gevent: 1
Gevent: 2
Gevent: 3
Gevent: 4
Gevent: 5
Gevent: 6
Gevent: 7
Gevent: 8
Gevent: 9
Gevent: 10
Gevent: 11
Process-1 Done 11
Gevent: 0
Gevent: 1
Gevent: 2
Gevent: 3
Gevent: 4
Gevent: 5
Gevent: 6
Gevent: 7
Gevent: 8
Gevent: 9
Gevent: 10
Gevent: 11
Process-2 Done 11
Gevent: 0
... ...

所以这是GEvent使用多处理的解决方案。


这个问题似乎是由于gevent.socket是非阻塞的,这意味着如果X字节在套接字上不立即可用,任何socket.recv_bytes(X)调用都会抛出这个错误。具体来说,gevent.socket的设计目的是永远不会阻塞插座。

multiprocessing的问题出现是因为它使用stdlib socket模块并期望它被阻塞,而在您使用monkey.patch_all()之后,socket模块被替换,并且multiprocessing.connection不是为处理新的异步行为而设计的。

您可以告诉monkey不要修补socket,但这意味着任何在应用程序中利用异步套接字的操作都可能因此而导致一些性能损失。

为此,请使用socket=False调用patch_allpatch_all(socket=False)

这不是一个理想的解决方案,因为首先使用gevent会使您失去很多好处。