Discord.py如何正确关闭机器人并断开数据库连接 含初始化疑问
问题解答
问题1:数据库初始化位置选择
首先明确两个事件的触发逻辑差异:
on_connect:每次机器人和Discord服务器成功建立TCP连接时触发,触发时机早于on_ready,临时断开重连时也会重复触发on_ready:机器人完成Discord内部数据(服务器、用户缓存等)全量同步后触发,同样可能因重连多次触发
你现有代码存在隐藏问题:在on_ready里给dbcon赋值没有加global声明,修改的是函数内局部变量,全局的dbcon始终是None,后续根本无法拿到连接进行关闭。
结论
- 优先使用discord.py 2.0新增的
setup_hook异步方法初始化数据库:该方法只会在机器人首次启动、连接Discord之前执行1次,不会因重连重复触发,是初始化全局资源的最优选择。 - 如果要在两个事件中选择,优先选
on_connect,但要做好幂等处理:每次触发时先判断全局dbcon是否已经存在,如果存在先关闭原有连接再新建,避免连接泄漏。不推荐用on_ready,如果你的数据库操作依赖Discord缓存数据,重连时缓存未同步完就操作会报错,且同样有重复创建连接的问题。
问题2:机器人关闭与数据库连接释放
首先明确on_disconnect不适合做该操作:这个事件每次和Discord连接断开时就会触发,包括临时断网、Discord服务器波动导致的短时间断开,此时如果直接关闭数据库连接,后续自动重连后机器人就没有可用的数据库连接了。
正确实现方式
- 被动关闭场景:
bot.run(TOKEN)是阻塞调用,只有当机器人完全停止运行后才会继续执行后续代码,你可以直接在这行代码后加数据库关闭逻辑:
bot.run(TOKEN) # 机器人停止后执行清理 if dbcon is not None: dbcon.close() print("数据库连接已关闭")
- 主动关闭场景:可以给机器人加一个管理员专属的关机命令,执行时先关数据库再停止机器人:
@bot.command() @commands.is_owner() async def shutdown(ctx): await ctx.send("机器人即将关闭") if dbcon is not None: dbcon.close() await bot.close()
- 异常退出场景:可以注册
atexit钩子确保进程退出时一定会关闭数据库连接:
import atexit def cleanup(): global dbcon if dbcon is not None: dbcon.close() atexit.register(cleanup)
修正后的完整示例代码
import os, sqlite3 import atexit from dotenv import load_dotenv from discord.ext import commands load_dotenv() TOKEN = os.getenv('DISCORD_TOKEN') bot = commands.Bot(command_prefix='!') dbcon = None # 初始化数据库,只会执行一次 @bot.event async def setup_hook(): global dbcon dbcon = sqlite3.connect('example.db') # 可以在这里加建表逻辑 print("数据库连接初始化完成") @bot.event async def on_ready(): print(f'{bot.user.name} has connected to Discord!') # 主动关机命令 @bot.command() @commands.is_owner() async def shutdown(ctx): await ctx.send("正在关闭机器人...") await bot.close() # 进程退出清理钩子 def cleanup(): global dbcon if dbcon is not None: dbcon.close() print("数据库连接已安全关闭") atexit.register(cleanup) bot.run(TOKEN)
内容的提问来源于stack exchange,提问作者Ziqi
相关产品推荐
相关产品推荐

