9

在进行 Gevent/Eventlet 猴子补丁之后 - 我可以假设每当 DB 驱动程序(例如redis-pypymongo)通过标准库(例如socket)使用 IO 时,它都是异步的吗?

因此,使用 eventlets 猴子补丁就足以使例如:redis-py在 eventlet 应用程序中非阻塞?

据我所知,如果我关心连接使用(例如,为每个greenlet 使用不同的连接)就足够了。但我想确定。

如果您知道还需要什么,或者如何在 Gevent/Eventlet 中正确使用 DB 驱动程序,请同时输入。

4

2 回答 2

18

如果以下所有条件都属实,您可以假设它将被神奇地修补。

  • 您确定 I/O 是建立在标准 Pythonsocket或其他eventlet/gevent猴子补丁之上的。没有文件,没有本机 (C) 套接字对象等。
  • 您传递aggressive=Truepatch_all(或patch_select),或者您确定该库不使用select或类似的东西。
  • 驱动程序不使用任何(隐式)内部线程。(如果驱动程序确实在内部使用线程,patch_thread 可能会工作,但可能不会。)

如果您不确定,它很容易测试——可能比通读代码并尝试解决它更容易。有一个greenlet,它只是做这样的事情:

while True:
    print("running")
    gevent.sleep(0.1)

然后让另一个对数据库运行慢查询。如果是monkeypatched,循环greenlet 将保持打印“运行”10 次/秒;如果没有,当程序在查询中被阻塞时,循环的 greenlet 将不会运行。

那么,如果您的驱动程序阻塞,您会怎么做?

最简单的解决方案是使用真正并发的线程池进行数据库查询。这个想法是您将每个查询(或批处理)作为线程池作业启动,并gevent在该作业完成时阻止您的。(对于非常简单的情况,您不需要很多并发查询,您可以只threading.Thread为每个查询生成一个,但通常您无法逃脱。)

如果驱动程序做了重要的 CPU 工作(例如,您正在使用运行进程内缓存的东西,甚至是像 sqlite 这样的整个进程内 DBMS),您希望这个线程池实际上是在进程之上实现的,否则GIL 可能会阻止您greenlets运行。否则(特别是如果您关心 Windows),您可能想要使用操作系统线程。(但是,这意味着您不能patch_threads();如果您需要这样做,请使用流程。)

如果您正在使用eventlet,并且想要使用线程,那么有一个名为的内置简单解决方案tpool可能就足够了。如果您正在使用gevent,或者您需要使用流程,这将不起作用。不幸的是,在真正的线程对象上阻塞一个greenlet(不阻塞整个事件循环)在eventletand之间有点不同gevent,并且没有很好地记录,但tpool源代码应该会给你这个想法。除了那部分之外,其余的只是使用concurrent.futuresfutures如果您在 2.x 或 3.1 中需要,请参阅 pypi)来执行 aThreadPoolExecutorProcessPoolExecutor. (或者,如果您愿意,您可以直接使用threadingmultiprocessing代替使用futures。)


你能解释一下为什么我应该在 Windows 上使用操作系统线程吗?

简短的总结是:如果你坚持使用线程,你几乎可以只编写跨平台代码,但如果你使用进程,你实际上是在为两个不同的平台编写代码。

首先,阅读模块的编程指南multiprocessing(“所有平台”部分和“Windows”部分)。幸运的是,DB 包装器不应该遇到这种情况。您只需要通过ProcessPoolExecutor. 而且,无论您是在游标操作级别还是查询级别进行包装,您的所有参数和返回值都将是可以腌制的简单类型。不过,这是您必须小心的事情,否则不会成为问题。

同时,Windows 的进程内同步对象开销很低,但进程间同步对象开销很大。(它还具有非常快的线程创建和非常慢的进程创建,但如果您使用的是池,这并不重要。)那么,您如何处理呢?我在创建操作系统线程以等待跨进程同步对象并发出信号时获得了很多乐趣,但是您对乐趣的定义可能会有所不同。

最后,tpool可以很容易地适应ppoolUnix,但在 Windows 上需要更多的工作(你必须了解 Windows 才能完成这项工作)。

于 2013-01-24T01:55:45.907 回答
2

abarnert 的回答是正确且非常全面的。我只想补充一点,eventlet 中没有“积极的”补丁,可能是 gevent 功能。此外,如果库使用select这不是问题,因为 eventlet 也可以对其进行修补。

实际上,在大多数情况下eventlet.monkey_patch(),您只需要。当然,必须在创建任何套接字之前完成。

如果您仍有任何问题,请随时打开问题或写信给 eventlet 邮件列表或 G+ 社区。所有相关链接都可以在http://eventlet.net/找到

于 2013-01-24T09:27:38.890 回答