python - 为什么在运行 Telegram 机器人几个小时后会得到 'MySQL server has gone away'?

标签 python mysql django mysql-python

我正在构建一个使用 mysqlclient 的 Django(3.0.5 版)应用程序(ver. 2.0.3) 作为数据库后端。此外,我编写了一个 Django 命令来运行使用 python-telegram-bot 编写的机器人。 API,因此该机器人的任务是无限期运行,因为它必须随时响应命令。
问题是大约 24 小时。运行机器人后(不一定一直空闲),我得到一个 django.db.utils.OperationalError: (2006, 'MySQL server has gone away')运行任何命令后的异常。
我绝对确定 MySQL 服务器一直在运行,并且在我收到此异常时仍在运行。 MySQL服务器版本为5.7.35 .
我的假设是某些 MySQL 线程会老化并关闭,因此在重用它们后它们不会得到更新。
有没有人遇到过这种情况并知道如何解决?

Traceback (most recent call last):
  File "/opt/django/gip/venv/lib/python3.6/site-packages/telegram/ext/dispatcher.py", line 555, in process_update
    handler.handle_update(update, self, check, context)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/telegram/ext/handler.py", line 198, in handle_update
    return self.callback(update, context)
  File "/opt/django/gip/gip/hospital/gipcrbot.py", line 114, in ayuda
    perfil = get_permiso_efectivo(update.message.from_user.id)
  File "/opt/django/gip/gip/hospital/telegram/funciones.py", line 33, in get_permiso_efectivo
    u = Telegram.objects.get(idtelegram=userid)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/query.py", line 411, in get
    num = len(clone)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/query.py", line 258, in __len__
    self._fetch_all()
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/query.py", line 1261, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/query.py", line 57, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1151, in execute_sql
    cursor.execute(sql, params)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 100, in execute
    return super().execute(sql, params)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/utils.py", line 90, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 86, in _execute
    return self.cursor.execute(sql, params)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/django/db/backends/mysql/base.py", line 74, in execute
    return self.cursor.execute(query, args)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 206, in execute
    res = self._query(query)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/MySQLdb/cursors.py", line 319, in _query
    db.query(q)
  File "/opt/django/gip/venv/lib/python3.6/site-packages/MySQLdb/connections.py", line 259, in query
    _mysql.connection.query(self, query)
django.db.utils.OperationalError: (2006, 'MySQL server has gone away')
我尝试过的东西
我已经尝试更改 Django settings.py 文件,因此我为 CONN_MAX_AGE 设置了一个显式值,我还为 MySQL 客户端设置了一个值 wait_timeout参数为 CONN_MAX_AGE低于 wait_timeout .
设置.py :
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
            'read_default_file': '/opt/django/gip/gip/gip/my.cnf',
        },
        'CONN_MAX_AGE': 3600,
    }
}
my.cnf :
[client]
...
wait_timeout = 28800
不幸的是,行为完全相同:我在大约 24 小时后收到异常。运行机器人后。

设置 CONN_MAX_AGENone也不会有任何区别。

我安装了mysql-server-has-gone-away @r-marolahy 提出的 python 包,但它也不会产生任何影响。运行后近 24 小时后,“消失”消息再次显示。

我还尝试了关闭旧连接的方法:
from django.db import close_old_connections

try:
    #do your long running operation here
except django.db.utils.OperationalError:
    close_old_connections()
    #do your long running operation here
仍然得到相同的结果。

最佳答案

由于服务器超时,MySQL 关闭连接时,django 发生了此错误。要启用持久连接,请设置 CONN_MAX_AGE为秒的正整数或将其设置为 None用于无限制的持久连接 ( source )。
更新 1:
如果上述建议的解决方案不起作用,您可能想尝试 mysql-server-has-gone-away包裹。我还没有尝试过,但在这种情况下可能会有所帮助。
更新 2:另一种尝试是尝试使用 try/except捕获这个的声明 OperationalError并使用 close_old_connections 重置连接.

from django.db import close_old_connections

try:
    #do your long running operation here
except django.db.utils.OperationalError:
    close_old_connections()
    #do your long running operation here
更新 3:如所述 here

The Django ORM is a synchronous piece of code, and so if you want to access it from asynchronous code you need to do special handling to make sure its connections are closed properly.


然而,Django ORM 使用 asgiref.sync.sync_to_async仅在 MySQL 关闭连接之前工作的适配器。在这种情况下使用 channels.db.database_sync_to_async (这是 SyncToAsync 版本,它在退出时清理旧的数据库连接)可能会解决这个问题。
您可以像下面这样使用它( source ):
from channels.db import database_sync_to_async

async def connect(self):
    self.username = await database_sync_to_async(self.get_name)()

def get_name(self):
    return User.objects.all()[0].name
或将其用作装饰器:
@database_sync_to_async
def get_name(self):
    return User.objects.all()[0].name
确保遵循安装说明 here第一的。

关于python - 为什么在运行 Telegram 机器人几个小时后会得到 'MySQL server has gone away'?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68937783/

相关文章:

Python Glob.glob : a wildcard for the number of directories between the root and the destination

python - 具有重复索引的 ix 选择行为不一致

python - 如何在没有换行符或空格的情况下打印

php - 如果最大时间戳大于其他最大时间戳

python - 使用 mongodb $push 和 $pull 的赞成(喜欢)功能

php - SQL 查询动态创建的 Cytoscape 节点

mysql - 使用外键自动递增

python - Django 单元测试数据库没有被拆除?

Django:如何在外键模型字段上定义动态初始值

Django动态模型设计