簡體   English   中英

SQLAlchemy 偵聽連接/斷開連接事件

[英]SQLAlchemy listen to connection/disconnection events

TL;DR :是否可以使用 SQLAlchemy ORM 監聽 DB Connect 和 Disconnect 事件

背景:

我有一個使用SQLAlchemy ORM 連接到 Postgres 數據庫的 Flask 應用程序。 ( Flask-SQLAlchemy )。 一切正常,下面的代碼沒有錯誤

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://<username>:<password>@<host>:<port>/<db>'
db = SQLAlchemy(app)

由於網絡問題,與數據庫的連接失敗或斷開。 訪問 DB 時會產生錯誤。 按照設計,SQLAlchemy 會在后台處理所有連接,並且在處理連接斷開和重新建立時無縫連接。

要求

為了監視和處理這些連接斷開,我需要觸發連接和斷開連接的事件

engine_connect文檔我發現可以綁定到引擎的connectcloseengine_connect事件

def connect(dbapi_connection, connection_record):
    print('DB Connected')


def disconnect(dbapi_connection, connection_record):
    print('DB Disconnected')


event.listen(db.engine, 'connect', connect)
event.listen(db.engine, 'engine_connect', connect)
event.listen(db.engine, 'close', connect)

同樣,還有其他事件,如close_detached first_connect等。

請注意,SQLAlchemy 建立了一個有點“懶惰的連接”,即只有在第一次調用數據庫時才會建立實際連接,因此需要調用db.engine.connect()來觸發connect事件。

預期行為

  1. 無論是在應用程序啟動時還是在生命周期期間,只要與 DB 建立連接。 回調函數connect()應該被觸發
  2. 每當由於數據庫關閉或網絡問題而丟失連接時,應觸發回調函數disconnect()

當前行為

  1. 在建立連接時,第一次調用connect() 無論連接是否斷開並重新connect()都不會再次調用connect()
  2. 在連接丟失時, disconnect()永遠不會被調用,我可能正在偵聽錯誤的事件。

為了測試,我在本地安裝了 PG 服務器,我正在啟動和停止服務。 並使用psql cli 驗證連接


我查看了 SQLAlchemy Core 以找到合適的事件和輪詢機制,但還沒有找到任何有效的方法。

SQLAlchemy 在不使用時不會查看連接,因此似乎需要輪詢。 這是否意味着我需要每 n 秒運行一次查詢,因為db.connection()在我的情況下在連接中途丟失時不會引發錯誤。

我理解 Flask 在單個進程中運行的局限性,本質上不支持異步調用。 我想知道即使不是Flask如何實現,需要什么樣的輪詢

我還沒有准備好接受像 SQLAlchemy 這樣成熟的工具不支持這一點,但我花了幾天時間還沒有找到可行的解決方案。 任何幫助或方向表示贊賞。

獎金問題:

在 Flask 啟動時檢查與 DB 的連接是否處於活動狀態的適當方法是什么。 目前,我在初始化后調用db.engine.connect()如果 DB 未連接,則會引發錯誤。

相關SO問題: 如何驗證SqlAlchemy引擎對象

有關 SQLAlchemy 如何處理斷開連接的更多信息 - 處理斷開連接

嗯,我是用Sqlalchemy的connect事件來設置時區的 當我手動重新啟動數據庫時,我可以確認連接事件可靠地工作。 我將 sqlalchemy 1.3.24 與 flask_sqlalchemy 2.5.1 和 MariaDB 10.3.17 一起使用。

我的代碼基本上說:

app.config.update(SQLALCHEMY_DATABASE_URI="mysql+pymysql://..."))
db = SQLAlchemy(app)
event.listen(db.engine, 'connect', set_time_zone)

如果我現在啟動數據庫、啟動應用程序並執行請求,我的連接回調將被調用。 如果我現在重新啟動數據庫,下一個請求將失敗(如預期):

sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1927, '連接被終止')

之后,下一個請求將再次調用connect回調,應用程序正常工作。

我唯一需要非常小心的是,當引發 OperationalError 時我回滾會話。 如果我不這樣做,則需要重新啟動燒瓶。

所以每個 api 端點看起來像這樣:

def whatever():
    from .. import db
    session = db.session

    try:
        # do your job
    except Exception:
        logger.error("Error: Doing whatever failed")
        print_exc()
        session.rollback()

暫無
暫無

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

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