
本文详解 Django 在异步代码中遭遇 MySQL 连接中断(如网络抖动、服务重启)时,如何正确触发重连机制——关键在于使用 sync_to_async 包装同步的 connection.connect(),而非仅调用 close_old_connections()。
本文详解 django 在异步代码中遭遇 mysql 连接中断(如网络抖动、服务重启)时,如何正确触发重连机制——关键在于使用 `sync_to_async` 包装同步的 `connection.connect()`,而非仅调用 `close_old_connections()`。
在 Django 的异步上下文(如 async def 视图或后台任务)中操作数据库时,若底层 MySQL 连接因网络中断、服务重启(如 (2013, 'Lost connection to MySQL server during query'))或超时((2006, 'Server has gone away'))而失效,Django 默认不会自动重连。尤其当连接对象被复用(如 connections['default'])且其内部状态已损坏时,仅调用 close_old_connections() 无法恢复连接——该函数在异步环境中作用有限,因为它不真正重建底层连接句柄,且 Django 的异步 ORM(如 aobjects.acount())底层仍会复用已失效的连接实例。
正确的解决方案是:显式触发连接对象的同步重连逻辑,并通过 sync_to_async 安全桥接到异步事件循环中。以下是可直接落地的实现:
from asyncio import sleep, run
from django import setup as django_setup
from django.db import connections
from django.contrib.auth.models import User
from django.db import OperationalError
from asgiref.sync import sync_to_async # ✅ 关键:必须导入此工具
django_setup()
def reconnect_db(alias='default'):
"""同步重连指定数据库连接"""
conn = connections[alias]
conn.close() # 先确保旧连接彻底关闭(推荐)
conn.connect() # 重新建立连接
async def test():
while True:
try:
count = await User.objects.all().acount()
print(f"User count: {count}")
except KeyboardInterrupt:
print("Stopped by user.")
break
except OperationalError as e:
error_code = e.args[0] if e.args else None
# 常见 MySQL 连接中断错误码
if error_code in (1053, 2006, 2013, 2026):
print(f"Connection lost ({error_code}): {e}. Attempting reconnect...")
try:
await sync_to_async(reconnect_db)() # ✅ 正确方式:同步重连 + 异步调度
print("✅ Reconnect successful.")
except Exception as re_conn_err:
print(f"❌ Reconnect failed: {re_conn_err}")
else:
raise # 非连接类错误,不应静默处理
except Exception as e:
print(f"Unexpected error: {type(e).__name__}: {e}")
print("Sleeping for 5s...")
await sleep(5)
run(test())
关键要点说明:
- ✅
sync_to_async(reconnect_db)()是核心:它将同步的conn.connect()安全地提交到线程池执行,避免阻塞事件循环,同时确保连接状态被真实刷新; - ❌
close_old_connections()在纯异步场景下无效:它主要清理同步线程中遗留的连接,对异步协程内复用的连接对象无实际修复作用; - ⚠️ 必须先
conn.close()再conn.connect():防止连接对象处于“半失效”状态,提升重连成功率; - ? 错误码需精准匹配:仅对明确指示连接中断的错误(如 2006/2013)触发重连,避免掩盖其他严重异常(如权限错误、SQL 语法错误);
- ? 生产环境建议封装为可复用的装饰器或中间件,例如:
async def with_auto_reconnect(func, *args, **kwargs): for attempt in range(3): try: return await func(*args, **kwargs) except OperationalError as e: if e.args[0] in (2006, 2013, 2026): await sync_to_async(reconnect_db)() continue raise raise RuntimeError("Max reconnect attempts exceeded")
通过以上方案,Django 异步应用即可稳健应对 MySQL 网络波动,在连接意外中断后自动恢复,保障长周期任务(如监控脚本、消息队列消费者)的持续可用性。











