如何在ProcessPool中处理SQLAlchemy连接?

时间:2016-09-21 10:06:08

标签: python sqlalchemy rabbitmq python-multiprocessing python-asyncio

我有一个从RabbitMQ代理获取消息的reactor,并触发工作方法在进程池中处理这些消息,如下所示:

Reactor

这是使用python asyncioloop.run_in_executor()concurrent.futures.ProcessPoolExecutor实现的。

现在我想使用SQLAlchemy访问worker方法中的数据库。大多数情况下,处理过程非常简单快捷。

反应堆在开始时每秒处理10-50条消息,因此不能为每个请求打开新的数据库连接。相反,我想为每个进程维护一个持久连接。

我的问题是:我该怎么做?我可以将它们存储在全局变量中吗? SQA连接池是否会为我处理这个问题?当反应堆停止时如何清理?

[更新]

  • 数据库是带有InnoDB的MySQL。

为什么选择带有流程池的模式?

当前实现使用不同的模式,其中每个使用者在其自己的线程中运行。不知何故,这不是很好。已经有大约200个消费者在他们自己的线程中运行,并且系统正在快速增长。为了更好地扩展,我们的想法是分离关注点并在I / O循环中使用消息并将处理委托给池。当然,整个系统的性能主要是I / O绑定。但是,处理大型结果集时CPU是一个问题。

另一个原因是“易用性”。虽然消息的连接处理和消耗是异步实现的,但是worker中的代码可以是同步且简单的。

很快就会发现,通过工作人员内部的持久网络连接访问远程系统是一个问题。这就是CommunicationChannels的用途:在worker中,我可以通过这些通道向消息总线发出请求。

我目前的一个想法是以类似的方式处理数据库访问:将语句通过队列传递到事件循环,然后将它们发送到数据库。但是,我不知道如何使用SQLAlchemy执行此操作。 入口点在哪里? 对象在通过队列时需要为pickled。如何从SQA查询中获取此类对象? 与数据库的通信必须异步工作,以免阻塞事件循环。我可以使用例如aiomysql作为SQA的数据库驱动程序?

3 个答案:

答案 0 :(得分:6)

如果您对如何实例化# db.py engine = create_engine("connection_uri", pool_size=1, max_overflow=0) DBSession = scoped_session(sessionmaker(bind=engine)) 进行实例化,假设您正在使用orm,则可以轻松满足每个进程池进程一个数据库连接的要求工人进程。

一个简单的解决方案是拥有一个全局session,您可以在请求中重复使用它:

# task.py
from db import engine, DBSession
def task():
    DBSession.begin() # each task will get its own transaction over the global connection
    ...
    DBSession.query(...)
    ...
    DBSession.close() # cleanup on task end

关于工人任务:

pool_size

参数max_overflowpool_size customize create_engine。DBSession.remove()使用的默认QueuePool将确保您的进程仅在每个进程中保持1个连接处于活动状态进程池。

如果您希望重新连接,可以使用recycle,它将从注册表中删除会话,并在下次使用DBSession时重新连接。您还可以使用Pool$(function() { alert('step1'); $("button.delete-category").click(function() { alert('step3'); console.log("sss"); return false; }); alert('step2'); }); 参数在指定的时间后重新连接。

在开发/部署期间,您可以使用AssertionPool,如果从池中签出多个连接,则会引发异常,请参阅switching pool implementations了解如何执行此操作。

答案 1 :(得分:1)

@roman:你有很好的挑战。

之前我的情况类似,所以这里是 2美分:除非此消费者"阅读" &# 34;写" 消息,不做任何实际处理,你可以重新设计这个消费者作为消费者/生产者消费消息,它将处理消息,然后将结果放入另一个队列,该队列(例如处理过的消息)可以被1..N非池化的异步进程读取,这些异步进程将打开其中的数据库连接'自己的整个生命周期。

我可以扩展我的答案,但我不知道这种方法是否符合您的需求,如果是这样,我可以为您提供有关扩展设计的更多细节。

答案 2 :(得分:0)

一种非常适合我的方法是使用网络服务器来处理和扩展流程池。 flask-sqlalchemy即使在其默认状态下也将保留连接池,并且不会在每个请求响应周期关闭每个连接。

asyncio执行程序只需调用url端点即可执行您的功能。额外的好处是因为所有进行工作的进程都在一个url后面,你可以轻松地跨多个机器扩展你的工作池,通过gunicorn或其他一些方法添加更多进程来扩展一个简单的wsgi服务器。另外,你获得了所有的容错能力。

缺点是您可能会通过网络传递更多信息。但是,正如您所说,问题是CPU绑定的,您可能会向数据库传递更多数据。