【问题标题】:Flask-sqlalchemy / uwsgi: DB connection problem when more than on process is usedFlask-sqlalchemy / uwsgi:使用多个进程时的数据库连接问题
【发布时间】:2020-08-07 06:49:58
【问题描述】:

我有一个使用 uwsgi 服务器在 Heroku 上运行的 Flask 应用程序,每个用户都连接到他自己的数据库。我已经实现了solution reported here for a very similar situation。特别是,我实现了连接注册表如下:

class DBSessionRegistry():
    _registry = {}

    def get(self, URI, **kwargs):
        if URI not in self._registry:
            current_app.logger.info(f'INFO - CREATING A NEW CONNECTION')
            try:
                engine = create_engine(URI,
                                       echo=False,
                                       pool_size=5,
                                       max_overflow=5)
                session_factory = sessionmaker(bind=engine)
                Session = scoped_session(session_factory)
                a_session = Session()
                self._registry[URI] = a_session
            except ArgumentError:
                raise Exception('Error')

        current_app.logger.info(f'SESSION ID: {id(self._registry[URI])}')
        current_app.logger.info(f'REGISTRY ID: {id(self._registry)}')
        current_app.logger.info(f'REGISTRY SIZE: {len(self._registry.keys())}')
        current_app.logger.info(f'APP ID: {id(current_app)}')

        return self._registry[URI]

在我的create_app() 中,我为应用分配了一个注册表:

app.DBregistry = DBSessionRegistry()

每当我需要与数据库通话时,我都会致电:

current_app.DBregistry.get(URI)

URI 取决于用户。如果我在一个进程中使用 uwsgi,这会很好地工作。随着更多的进程,

[uwsgi]
processes = 4
threads = 1

有时它会卡在某些请求上,返回 503 错误代码。我发现当请求由uwsgi中的不同进程处理时会出现问题。这是日志的摘录,我对此进行了评论以说明问题:

# ... EVERYTHING OK UP TO HERE.
# ALL PREVIOUS REQUESTS HANDLED BY PROCESS pid = 12
INFO in utils: SESSION ID: 139860361716304
INFO in utils: REGISTRY ID: 139860484608480
INFO in utils: REGISTRY SIZE: 1
INFO in utils: APP ID: 139860526857584
# NOTE THE pid IN THE NEXT LINE...
[pid: 12|app: 0|req: 1/1] POST /manager/_save_task => 
generated 154 bytes in 3457 msecs (HTTP/1.1 200) 4 headers in 601 
bytes (1 switches on core 0)
# PREVIOUS REQUEST WAS MANAGED BY PROCESS pid = 12
# THE NEXT REQUEST IS FROM THE SAME USER AND TO THE SAME URL.
# SO THERE IS NO NEED FOR CREATING A NEW CONNECTION, BUT INSTEAD...
INFO - CREATING A NEW CONNECTION
# TO THIS POINT, I DON'T UNDERSTAND WHY IT CREATED A NEW CONNECTION.
# THE SESSION ID CHANGES, AS IT IS A NEW SESSION
INFO in utils: SESSION ID: 139860363793168    # <<--- CHANGED
INFO in utils: REGISTRY ID: 139860484608480
INFO in utils: REGISTRY SIZE: 1
# THE APP AND THE REGISTRY ARE UNIQUE
INFO in utils: APP ID: 139860526857584
# uwsgi GIVES UP...
*** HARAKIRI ON WORKER 4 (pid: 11, try: 1) ***
# THE FAILED REQUEST WAS MANAGED BY PROCESS pid = 11
# I ASSUME THIS IS WHY IT CREATED A NEW CONNECTION
HARAKIRI: -- syscall> 7 0x7fff4290c6d8 0x1 0xffffffff 0x4000 0x0 0x0 
0x7fff4290c6b8 0x7f33d6e3cbc4
HARAKIRI: -- wchan> poll_schedule_timeout
HARAKIRI !!! worker 4 status !!!
HARAKIRI [core 0] - POST /manager/_save_task since 1587660997
HARAKIRI !!! end of worker 4 status !!!
heroku[router]: at=error code=H13 desc="Connection closed without 
response" method=POST path="/manager/_save_task"
DAMN ! worker 4 (pid: 11) died, killed by signal 9 :( trying respawn ...
Respawned uWSGI worker 4 (new pid: 14)
# FROM HERE ON, NOTHINGS WORKS ANYMORE

这种行为在多次尝试中是一致的:当 pid 更改时,请求失败。即使在create_engine 函数中使用pool_size = 1,问题仍然存在。没有问题,而是 uwsgi 与一个进程一起使用。

我很确定这是我的错,有些事情我不知道或者我不明白 uwsgi 和/或 sqlalchemy 是如何工作的。你能帮帮我吗?

谢谢

【问题讨论】:

  • 嘿,看来你需要在worker之间共享内存。检查这个问题,它没有解决方案,但它确实解释了原因stackoverflow.com/questions/54133550/…
  • @univerio 我被注册表的唯一 ID 愚弄了。

标签: flask heroku sqlalchemy flask-sqlalchemy uwsgi


【解决方案1】:

什么是hapeening 是你试图在进程之间共享内存。 这些帖子中有一些解释。

(is it possible to share memory between uwsgi processes running flask app?).

(https://stackoverflow.com/a/45383617/11542053)

您可以使用额外的层将会话存储在应用程序之外。

为此,您可以使用 uWsgi 的 SharedArea(https://uwsgi-docs.readthedocs.io/en/latest/SharedArea.html),它的级别非常低,或者您可以使用其他方法,例如 uWsgi 的缓存(https://uwsgi-docs.readthedocs.io/en/latest/Caching.html)

希望对你有帮助。

【讨论】:

  • 谢谢,我尝试使用惰性应用程序,所以每个进程都以“它自己的应用程序”开头(我在日志消息中看到应用程序为每个进程启动并且我有不同的 current_app id )。但是,问题仍然存在,但为什么呢?在这种情况下,应用程序不是独立的吗?
  • 这是因为您的应用程序是独立的。如果您第一次发出请求并且由 worker1 处理,它将在 worker1 的应用程序上创建一个会话,如果您的第二个请求由 worker2 处理,则无法访问 worker1 的数据,您将创建另一个会话,这次是在 worker2 中。
  • 我理解与多个进程和一个单个应用程序的冲突(就像我最初的情况一样),但是对于lazy_apps,我们有不同的进程 --> 不同的应用程序 --> 不同的注册表 --> 不同的会话,所以在这里应该没问题,因为没有会话共享,数据在DB上。
  • 您要关闭会话吗?我经常有这个问题。引擎盖下似乎出现了僵局。
猜你喜欢
  • 2016-03-19
  • 1970-01-01
  • 1970-01-01
  • 2017-05-07
  • 2016-08-21
  • 2018-06-13
  • 2017-07-07
  • 2018-03-26
  • 1970-01-01
相关资源
最近更新 更多