【问题标题】:'MySQL has gone away' with Django ORM used outside Django project在 Django 项目之外使用 Django ORM 时“MySQL 已经消失”
【发布时间】:2016-10-28 17:36:10
【问题描述】:

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

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

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

在网站闲置一段时间后,我在 Tornado 服务中收到了臭名昭著的“2006:MySQL 已消失”错误。我做了一些挖掘,看来罪魁祸首是 MySQL 断开了连接。

然而,这让我感到困惑:我使用的 Django ORM 与 Django 应用程序使用的相同,但 Django 应用程序本身从未引发此错误。此外,我的理解是,如果发生此错误,Django ORM 会自动重新连接,这解释了为什么我在 Django 应用程序中没有看到此错误。那么,为什么我会在 Tornado 中发生这种情况?

就目前而言,让我的 Tornado 实例恢复活力的唯一方法是重新启动运行它的 gunicorn 进程。重新启动后,Tornado 将毫无问题地工作,直到我让它保持几个小时。

我读过这篇文章: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 会抛出这个错误?

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

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

【问题讨论】:

    标签: mysql django


    【解决方案1】:

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

    【讨论】:

    猜你喜欢
    • 2018-01-17
    • 2017-04-02
    • 2015-01-13
    • 2011-06-14
    • 1970-01-01
    • 1970-01-01
    • 2017-09-27
    • 2012-09-21
    • 1970-01-01
    相关资源
    最近更新 更多