'MySQL has gone away' 在 Django 项目之外使用 Django ORM



我有一个Django应用程序和一个Tornado服务在同一台服务器上运行。在Tornado服务中,我使用Django ORM访问MySQL数据库。Django 应用程序使用相同的数据库。

我们的 Django Web 应用程序中的每个页面,当呈现在客户端上时,都会与 Tornado 服务建立持久 (WebSocket( 连接。该服务使用 Django ORM 来检索数据并将其返回给客户端。

该网站使用量不大,有时后续请求之间可能会经过几个小时甚至一两天。

在网站闲置一段时间后,我在Tornado服务中收到臭名昭著的"2006:MySQL已经消失"错误。我做了一些挖掘,似乎罪魁祸首是MySQL丢弃的连接。

然而,这让我感到困惑:我正在使用与 Django 应用程序相同的 Django ORM,但 Django 应用程序本身从未触发此错误。此外,我的理解是,如果发生此错误,Django ORM 会自动重新连接,这解释了为什么我在 Django 应用程序中看不到此错误。那么,为什么它会发生在龙卷风中呢?

就目前而言,让我的 Tornado 实例恢复活力的唯一方法是重新启动运行它的 gunicorn 进程。重新启动后,龙卷风将正常工作而不会打嗝,直到我离开它几个小时。

我已经读过这个:https://code.djangoproject.com/ticket/21597#comment:29 和StackOverflow上类似问题的一些答案,但我不认为增加MySQL上的超时可以解决这个问题,它只是使它不太可能发生。(无论如何,无论如何,我的wait_timeout都设置为一个相当大的值 - 28800.另一方面,为了摆脱它,max_allowed_packet设置为16777216,在这里不太可能成为问题,因为它失败的调用基本上只是从数据库中检索会话对象。

一位 Django 核心开发人员在上面的链接中提出的另一种解决方案是显式关闭连接:from django.db import connection; connection.close()当您知道您的程序将长时间闲置时。我实际上并不知道这一点,因为我无法预测我的客户显然会多久请求一次页面。对我来说,在请求送达后关闭连接似乎也有些矫枉过正。如果没有其他方法,我当然会这样做,但这里似乎有些不对劲,因为似乎 Django 应该透明地重新连接(我实际上在某处读过它,但不完全确定这是真的。

我想我的主要问题是,如果需要关闭连接,为什么我的 Django 应用程序看起来很好?我没有关闭 Django 应用程序中的任何连接,但它永远不会引发相同的错误。如果不需要关闭连接并且由 Django 自动完成,那么为什么 Tornado 服务中使用的 Django ORM 会抛出此错误?

仅供参考,这是导致龙卷风应用程序中出现此错误的代码。

def get_django_session(handler):
    if not hasattr(handler, '_session'):
        engine = importlib.import_module(
                django.conf.settings.SESSION_ENGINE)
        session_key = handler.get_cookie(django.conf.settings.SESSION_COOKIE_NAME)
        handler._session = engine.SessionStore(session_key)
    return handler._session

def get_current_user(handler):
    # get_user needs a django request object, but only looks at the session
    class Dummy(object):
        pass
    django_request = Dummy()
    django_request.session = get_django_session(handler)
    user = django.contrib.auth.get_user(django_request)  # 2006: MySQL has gone away
    if user.is_authenticated():
        return user
    else:
        return None

在每个请求之前和之后(通过request_startedrequest_finished信号(,Django 调用 close_old_connections() ,它会测试每个连接并在无法使用时关闭它。可以在对连接执行任何操作之前和之后调用此方法。这不会关闭仍然可用的连接,因此每次在 Tornado 中执行某些操作时都不会有新连接。

最新更新