Python 炼金术课程不同步

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

我有一个Flask REST API,使用gunicorn/nginx堆栈运行。为API运行的每个线程设置一次全局SQLAlchemy会话。我设置了一个端点/测试/来运行API的单元测试。一个测试发出POST请求向数据库添加内容,然后有一个finally:子句要清理:

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请求
侦听器中运行
创建新会话
。(命名不精确,但你知道了。)这样,每个工作者将获得自己的连接池,每个请求将获得自己的会话。