Python 炼金术课程不同步
我有一个Flask REST API,使用gunicorn/nginx堆栈运行。为API运行的每个线程设置一次全局SQLAlchemy会话。我设置了一个端点/测试/来运行API的单元测试。一个测试发出POST请求向数据库添加内容,然后有一个finally:子句要清理:Python 炼金术课程不同步,python,multithreading,session,sqlalchemy,gunicorn,Python,Multithreading,Session,Sqlalchemy,Gunicorn,我有一个Flask REST API,使用gunicorn/nginx堆栈运行。为API运行的每个线程设置一次全局SQLAlchemy会话。我设置了一个端点/测试/来运行API的单元测试。一个测试发出POST请求向数据库添加内容,然后有一个finally:子句要清理: def test_something(): try: url = "http://myposturl" data = {"content" : "test post"} he
def test_something():
try:
url = "http://myposturl"
data = {"content" : "test post"}
headers = {'content-type': 'application/json'}
result = requests.post(url, json=data, headers=headers).json()
validate(result, myschema)
finally:
db.sqlsession.query(MyTable).filter(MyTable.content == "test post").delete()
db.sqlsession.commit()
问题是,发出POST请求的线程现在在其会话中有一个“test POST”对象,但数据库没有这样的对象,因为运行测试的线程从数据库中删除了该对象。因此,当我向服务器发出GET请求时,大约四分之一(我有四个gunicorn工作人员)会得到“test post”对象,四分之三没有。这是因为每个线程都有自己的会话对象,它们正在失去同步,但我真的不知道该怎么办
以下是我的SQLAlchemy会话设置:
def connectSQLAlchemy():
import sqlalchemy
import sqlalchemy.orm
engine = sqlalchemy.create_engine(connection_string(DBConfig.USER, DBConfig.PASSWORD, DBConfig.HOST, DBConfig.DB))
session_factory = sqlalchemy.orm.sessionmaker(bind=engine)
Session = sqlalchemy.orm.scoped_session(session_factory)
return Session()
# Create a global session for everyone
sqlsession = connectSQLAlchemy()
请使用flask sqlalchemy如果您正在使用flask,它会为您处理会话的生命周期 如果您坚持自己做,正确的模式是为每个请求创建一个会话,而不是创建一个全局会话。你应该做什么
Session = scoped_session(session_factory, scopefunc=flask._app_ctx_stack.__ident_func__)
return Session
而不是
Session = scoped_session(session_factory)
return Session()
做
session = Session()
每次你需要治疗的时候。通过
scoped_session
和scopefunc
,这将在每个请求中返回不同的会话,但在同一请求中返回相同的会话。解决了这个问题。我所做的是在我的应用程序的_uinit__u; py.py中向请求添加一个设置和拆卸:
@app.before_request
def startup_session():
db.session = db.connectSQLAlchemy()
@app.teardown_request
def shutdown_session(exception=None):
db.session.close()
仍在使用my db模块中的全局会话对象:
db.py:
....
session = None
....
scoped_会话
处理不同的线程,我认为
如果出于某种原因,这是一种可怕的方法,请给出建议=c) 我照你说的做了,我将self.session=db.session()放在一个新的APIResource(Resource)类的init函数中,但我遇到了同样的问题。。。我向数据库中添加了一些内容,并在单独的线程中删除它,但添加线程会记住这些内容。@Scott你是说在同一个请求中有多个线程?不,只有gunicorn线程。我认为这个解决方案的问题在于,我需要在请求结束时的某个地方中断会话。@Scott是的。你以前没这么做过?再次,我强烈推荐使用flask sqlalchemy,因为它能为您解决这一问题。@univerio我很难理解
scoped_会话(session_factory)
和scoped_会话(session_factory,scopefunc=flask.\u app_ctx_stack.\uu ident_func_u)之间的有效区别是什么。在前者中,我们使用线程本地存储—调用Session()
的每个线程都将获得自己的会话。对Flask的每个请求都将创建一个新线程,从而获得它自己的会话。为什么这实际上与后者不同?scoped_session
不会像这样处理多个线程。一旦你有多个线程,你就会遇到麻烦。你需要以每进程方式连接,例如4个进程=4个DB连接,并以每请求方式打开会话1个请求=1个新会话。@alextsil“以每进程方式连接”,因此DB.connectSQLAlchemy()
创建连接和会话?@Him否,您需要在@app.before.server>侦听器中运行create\u引擎
,然后在@app.before请求
侦听器中运行创建新会话
。(命名不精确,但你知道了。)这样,每个工作者将获得自己的连接池,每个请求将获得自己的会话。