1. 背景
基于 Python django 的第三方库 Django-apscheduler,用来提供 Python 的后台定时程序,实现每隔24小时调用 Django 的 orm 来对数据库进行数据处理。
2. 出现的问题
在间隔24小时之后,执行定时调度的后台线程进行数据库查询时,出现报错:
DB error executing ‘_get_jobs’ ((2006, “MySQL server has gone away (BrokenPipeError(32, ‘Broken pipe’))”)). Retrying with a new DB connection…
Run time of job “mission (trigger: cron[day=’*’, hour=‘0’, minute=‘0’, second=‘0’], next run at: 2022-02-07 00:00:00 CST)” was missed by 0:00:01.048758
3. 原因排查的过程
Django 跑批任务错误 (2006, ‘MySQL server has gone away’) 和 MySQL server has gone away 设置 mysql 连接自动断开,初步判断是由于长时间没有操作数据库导致连接被数据库断开而导致的报错。
因为 Mysql 的 wait_timeout 默认是8个小时,而我项目中定时任务是24小时执行一次,且一次执行需要的时间只需要几秒钟,当第一次执行的时候,Django 和 Mysql 建立的一个连接会自动保持8小时的连接,但是8小时之后 Mysql 会断开这个连接,而第二次执行任务的时候Django 还用上次的连接去连接 mysql,因此得到一个 gone away 的结果。
4. 解决方案
一、加长 wait_timeout
在数据库的配置文件内配置超时时间 wait_timeout,还有设置 max_allow_pocket 的(对于 sql 语句长的、数据量大的有效),网上有很多教程,这种方案增加 mysql 的资源开销,此处不推荐。
二、修改 Django-apscheduler 源码
自测有效
找到 ${python_path}/lib/python3.7/site-packages/django_apscheduler/jobstores.py
文件
找到 def _get_jobs(self, **filters)
函数(大约在 288 行)
在这个函数里加一行代码 db.close_old_connections()
关掉过期的旧连接,建立新连接
def _get_jobs(self, **filters):
db.close_old_connections()
jobs = []
failed_job_ids = set()
job_states = DjangoJob.objects.filter(**filters).values_list("id", "job_state")