SQLAlchemy 连接在 AWS MySQL RDS 上挂起并进行故障转移

SQLAlchemy connection hangs on AWS MySQL RDS reboot with failover

我们有一个 Python 服务器,它使用 SQLAlchemy 来 read/write 来自 AWS MySQL MultiAZ RDS 实例的数据。

我们遇到了一种我们希望避免的行为,即每当我们触发故障转移重新启动时,一个已经打开然后发出语句的连接会无限期挂起。虽然根据 AWS 文档这是意料之中的事情,但我们希望 Python MySQL 连接器能够应对这种情况。

我们在网上找到的最接近的案例是 google groups thread,它讨论了这个问题并提供了有关 Postgres RDS 的解决方案。

例如,以下脚本在启动故障转移重启时将无限期挂起(取自上述 google 组线程)。

from datetime import datetime
from time import time, sleep
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.scoping import scoped_session
from sqlalchemy.ext.declarative import declarative_base

import logging

current_milli_time = lambda: int(round(time() * 1000))
Base = declarative_base()

logging.basicConfig(format='%(asctime)s %(filename)s %(lineno)s %(process)d %(levelname)s: %(message)s', level="INFO")

class Message(Base):
    __tablename__ = 'message'
    id = Column(Integer, primary_key=True)
    body = Column(String(450), nullable=False)

engine = create_engine('mysql://<username>:<password>@<db_host>/<db_name>',echo=False, pool_recycle=1800,)
session_maker = scoped_session(sessionmaker(bind=engine, autocommit=False, autoflush=False))
session = session_maker()

while True:
    try:
        ids = ''
        start = current_milli_time()
        for msg in session.query(Message).order_by(Message.id.desc()).limit(5):
            ids += str(msg.id) + ', '
            logging.info('({!s}) (took {!s} ms) fetched ids: {!s}'.format(datetime.now().time().isoformat(), current_milli_time() - start, ids))

        start = current_milli_time()
        m = Message()
        m.body = 'some text'
        session.add(m)
        session.commit()
        logging.info('({!s}) (took {!s} ms) inserted new message'.format(datetime.now().time().isoformat(), current_milli_time() - start))

    except Exception, e:
        logging.exception(e)
        session.rollback()
    finally:
        session_maker.remove()

    sleep(0.25)

我们试过连接超时,但问题似乎与已经打开的连接有关,一旦 AWS 切换到故障转移实例,该连接就会挂起。

我们的问题是 - 有没有人遇到过这个问题或有可能的方向值得检查?

恕我直言,使用 SQL 连接器超时来处理 switchcover 就像黑魔法一样。每个连接器的行为总是不同且难以诊断。

如果您再次阅读@univerio 评论,AWS 将为相同的 RDS 端点名称重新分配一个新的 IP 地址。在进行切换时,您的 RDS 端点名称和旧 IP 地址仍在您的服务器实例 DNS 缓存中。所以这是一个 DNS 缓存问题,这就是 AWS 要求您 "clean up...." 的原因。

除非您重新启动 SQLAlchemy 以再次读取 DNS,否则会话无法知道发生了什么并动态切换它。最糟糕的是,问题可能发生在 SQLAlchemy 使用的连接器中。

恕我直言,不值得在代码内部处理切换。我将只订阅像 lambda 这样的 AWS 服务,它可以在切换事件时起作用,触发应用服务器重新启动连接,这应该反映新的 IP 地址。