簡體   English   中英

如何解決 Flask-SQLAlchemy 數據庫會話超時(避免“MySQL 服務器已經消失”)

[英]How to get around Flask-SQLAlchemy database session timing out (avoiding "MySQL server has gone away")

我在這里多次看到這個問題,但答案對我沒有幫助。

根據我的理解,Flask-SQLAlchemy 在 Flask 請求開始時連接到數據庫,並在完成后拆除此會話。 但是如果這個請求需要很長時間呢?

例如,我的應用程序接受一個 POST 請求,該請求啟動一個需要寫入數據庫的長時間運行的 Celery 任務。 MySQL 服務器在一段時間后“消失”,使得在任務結束時寫回數據庫不起作用。

我在線閱讀以設置options["pool_pre_ping"] = True以緩解此類問題(使用此處找到的子類化 SQLAlchemy 解決方法),但這沒有區別。 發生了同樣的錯誤,幾乎似乎沒有任何設置實際發生變化。

我認為解決方案是重新考慮任務如何寫入數據庫。 目前,我正在使用來自任務請求的相同session 如果這是錯誤的有什么想法嗎?

我認為您對重新思考任務如何寫入數據庫有正確的想法。

讓 Flask 路由將任務附加到隊列可能是謹慎的。 一旦任務進入隊列,Flask 路由就可以返回,因此 Web 請求上下文將被關閉。

然后,您可以在后台運行另一個 python(非燒瓶)程序,該程序不斷從該隊列中讀取,並從中彈出最新的任務,然后執行這些任務並將它們保存到數據庫中。

這可以通過使用來完成 也許您已經在使用 Celery 來調用異步函數? 這是一篇關於如何創建這樣的 Celery 任務並將其與 Flask 路由一起使用的博客文章https://blog.miguelgrinberg.com/post/using-celery-with-flask

您是對的,當 Web 請求上下文關閉時,Flask-SQLAlchemy 會拆除數據庫會話,這在 Flask 路由返回時發生。

為了防止燒瓶的SQLAlchemy從關閉會話不應傳遞您的session從燒瓶路線異步芹菜功能。 相反,您應該在 celery 異步函數中“創建一個新會話”(調用會話注冊表)。

事實上,這是在 Web 應用程序中使用來自 SQLAlchemy 的會話時的一般原則。 您想避免傳遞會話對象。 SQLAlchemy 通過使用注冊表模式創建一個返回“線程本地”會話對象的會話注冊表來提升這個主體。 “線程本地”會話的這種想法意味着當您從會話注冊表中獲取會話時,您確保該會話僅在當前線程中使用。 由於您最終要創建一個新線程來執行長時間運行的任務,因此您可以依靠 Session Registry 為您提供一個不會受到 Web 請求關閉影響的線程,因為 Web 請求是在不同的線程中處理的.

Flask-SQLAlchemy 默認實現了這個會話注冊表。 例如,您可能有一個路由和芹菜任務,如下所示:

from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from celery import Celery

app = Flask(__name__)
db = SQLAlchemy(app)

app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'

celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)


@celery.task
def do_stuff_that_takes_10_minutes(payload):
    session = db.session  # Gets a thread local session from the Session Registry that is different from the session obj you used in the route because this function will be executed in its own thread.
    # some long running task here
    return result

@app.route('/long_running_task', methods=['GET'])
def long_running_task():
  session = db.session  # Get a thread local session from the Session Registry that will close once this web request is complete

  # use this session here and do stuff

  # Call this function, which Celery will spawn a new thread to do
  # Don't pass the above session object into this function, we want this
  # session obj to stay only within this thread.
  do_stuff_that_takes_10_minutes.delay()

  return "Executing task, could take 10 minutes or more."

關閉會話但保留對象。 編輯它們。 然后使用merge 確保設置expire_on_commitFalse

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM