Gevent/Eventlet猴子修补DB驱动程序

Rob*_*mba 9 python asynchronous pymongo gevent eventlet

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

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

据我所知,如果我注意连接使用(例如,为每个greenlet使用不同的连接),这应该足够了.但我想确定.

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

aba*_*ert 18

如果满足以下所有条件,您可以假设它将被神奇地修补.

  • 您确定I/O是基于标准Python socket或其他eventlet/ geventmonkeypatches构建的.没有文件,没有本机(C)套接字对象等.
  • 您传递aggressive=Truepatch_all(或patch_select),或者您确定该库不使用select或类似的任何东西.
  • 驱动程序不使用任何(隐式)内部线程.(如果驱动程序内部使用线程,patch_thread 可能会有效,但可能没有.)

如果你不确定,它很容易测试 - 可能比阅读代码并试图解决它更容易.有一个greenlet只做这样的事情:

while True:
    print("running")
    gevent.sleep(0.1)
Run Code Online (Sandbox Code Playgroud)

然后让另一个对数据库运行慢查询.如果它是monkeypatched,循环greenlet将保持打印"运行"10次/秒; 如果没有,则在查询阻止程序时,循环greenlet将无法运行.

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

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

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

如果您正在使用eventlet,并且您想使用线程,则可以使用内置的简单解决方案tpool.如果您正在使用gevent,或者您需要使用流程,这将无效.不幸的是,在真正的线程对象上阻塞greenlet(不阻塞整个事件循环)在eventlet和之间有点不同gevent,并且没有很好地记录,但是tpool源代码应该给你一个想法.除此之外的部分,剩下的只是使用concurrent.futures(见futuresPyPI上,如果你需要这2.x或3.1),以在执行任务ThreadPoolExecutorProcessPoolExecutor.(或者,如果您愿意,可以直接使用threadingmultiprocessing代替使用futures.)


你能解释为什么我应该在Windows上使用OS线程吗?

快速摘要是:如果您坚持使用线程,您几乎可以编写跨平台代码,但如果您转到流程,则可以有效地为两个不同的平台编写代码.

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

同时,Windows的进程内同步对象的开销非常低,但其进程间的开销却很高.(它也有非常快的线程创建和非常慢的进程创建,但如果你使用池,这并不重要.)那么,你如何处理它?我有很多乐趣创建操作系统线程来等待跨进程同步对象并发出greenlet的信号,但你对乐趣的定义可能会有所不同.

最后,tpool可以简单地ppool适用于Unix,但它在Windows上需要更多的工作(你必须了解Windows才能完成这项工作).