You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复discord.py机器人无报错但响应延迟的运行异常问题

问题根因分析
  • 同步数据库查询阻塞异步事件循环:你使用的logs_info.find_one是MongoDB同步驱动的查询方法,属于同步阻塞操作。discord.py完全基于异步事件循环运行,任何同步阻塞操作都会卡住整个事件循环的执行,期间所有指令接收、事件处理、消息回复都会暂停。你的旧机器人在86个服务器运行,on_member_update会在任意服务器成员的昵称、状态、角色等属性变更时触发,单日触发量可达数千甚至数万次,大量阻塞操作堆积后就会出现几分钟才回复指令的情况,甚至删除代码后短时间内还是卡顿,是因为事件循环中还有大量堆积的未处理事件,重启进程即可立刻消除堆积。新机器人因为加入的服务器少,事件触发量极低,阻塞的负面影响没有暴露,所以运行正常,和token本身没有任何关系。
  • 无效事件无前置过滤:on_member_update会响应所有成员属性变更事件,你当前的逻辑仅需要处理角色变更场景,但代码是先执行数据库查询,再判断角色是否变化,90%以上的数据库查询都是无效的,额外增加了大量阻塞开销。
  • 角色差集计算效率低:使用遍历列表的方式查找变更的角色,时间复杂度为O(n²),成员角色数量较多时会额外增加执行耗时。
  • 重复初始化时区对象:每次事件触发都重新实例化pytz.timezone('Asia/Kolkata'),属于不必要的重复操作。
优化方案
  • 替换为异步数据库驱动:将MongoDB同步驱动替换为异步驱动motor,使用异步查询方法await logs_info.find_one(...),避免阻塞事件循环,仅需修改连接逻辑和查询前加await,不需要调整已有数据结构。
  • 增加事件前置过滤:进入逻辑后首先判断before.roles == after.roles,如果角色没有变化直接返回,不需要执行后续的数据库查询等操作。
  • 优化角色差集计算:使用集合求差的方式查找变更角色,时间复杂度降到O(n)。
  • 全局初始化时区对象:将时区实例化操作移到Cog类的初始化方法或者全局变量中,仅初始化一次即可。
  • 增加日志通道存在性判断:获取到日志通道后提前判断是否为None,避免后续发送消息时报错。
优化后代码示例
import pytz
from datetime import datetime
import discord
from discord.ext import commands
from random import randint

# 全局初始化时区,仅执行一次
IST = pytz.timezone('Asia/Kolkata')

class YourCogName(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        # logs_info在这里初始化motor异步集合

    @commands.Cog.listener()
    async def on_member_update(self, before, after):
        # 前置过滤:角色没变化直接返回,跳过所有后续逻辑
        if before.roles == after.roles:
            return
        
        # 只在角色有变化时才执行查询
        try:
            server_id = after.guild.id
            # 异步查询,加await
            log_config = await logs_info.find_one({'id': server_id})
            if not log_config or 'action_logs' not in log_config:
                return
            channel_id = int(log_config['action_logs'])
            logchannel = self.bot.get_channel(channel_id)
            if not logchannel:
                return
        except Exception:
            return
        
        time_now = datetime.now(IST).strftime('%Y-%m-%d %H:%M:%S')
        # 用集合求差集找变更角色,效率更高
        if len(before.roles) < len(after.roles):
            new_role = next(iter(set(after.roles) - set(before.roles)))
            embed = discord.Embed(
                title="角色已添加",
                description=f"已为 {after.mention} 授予角色 {new_role.mention}",
                colour=randint(0, 0xffffff)
            )
            embed.add_field(name="添加时间", value=time_now)
        else:
            removed_role = next(iter(set(before.roles) - set(after.roles)))
            embed = discord.Embed(
                title="角色已移除",
                description=f"已从 {after.mention} 移除角色 {removed_role.mention}",
                colour=randint(0, 0xffffff)
            )
            embed.add_field(name="移除时间", value=time_now)
        
        embed.set_thumbnail(url=after.guild.icon.url if after.guild.icon else None)
        embed.set_footer(text=self.bot.user.name, icon_url=self.bot.user.avatar.url if self.bot.user.avatar else None)
        embed.set_author(name=after.name, icon_url=after.avatar.url if after.avatar else None)
        await logchannel.send(embed=embed)

内容的提问来源于stack exchange,提问作者Daniel Jebarson K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 08:24:03