Discord.py结合AsyncIOScheduler出现心跳阻塞警告的问题求助
解决Discord.py机器人心跳阻塞警告问题
问题根源
你遇到的discord.gateway: Shard ID None heartbeat blocked for more than 10 seconds警告,本质是事件循环被长时间阻塞导致Discord网关的心跳包无法按时发送。你的read_site函数里存在多个同步阻塞操作:
- 同步HTTP请求:
requests.get是阻塞式的,网络请求过程中整个事件循环会卡住 - 同步文件读写:
open()/read()/write()都是同步操作,会占用事件循环 - 网页解析:
BeautifulSoup的HTML解析属于CPU密集型操作,同样会阻塞事件循环
这些操作单次执行可能耗时不长,但高频重复执行后,积累的负载会导致事件循环无法及时响应Discord的心跳检测,最终触发警告。misfire_grace_time仅用于处理任务错过调度的情况,无法解决事件循环阻塞的核心问题。
解决方案
将所有阻塞操作替换为异步实现,确保事件循环始终能及时处理Discord的心跳任务:
1. 安装必要的异步库
pip install aiohttp aiofiles
2. 修改代码为全异步实现
import os import discord import aiohttp from bs4 import BeautifulSoup import asyncio from apscheduler.schedulers.asyncio import AsyncIOScheduler import aiofiles intents = discord.Intents.default() intents.message_content = True intents.members = True client = discord.Client(intents=intents) token = "token" url = "site url" params = { 'id': 'id', } user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36' headers = { 'User-Agent': user_agent } async def read_site(): # 异步HTTP请求,不阻塞事件循环 async with aiohttp.ClientSession() as session: async with session.get(url, params=params, headers=headers) as resp: content = await resp.read() # 将HTML解析放到线程池,避免CPU密集操作阻塞事件循环 soup = await asyncio.to_thread(BeautifulSoup, content, 'html.parser') article_nums = soup.find("tbody").find_all("tr", class_="ub-content us-post") # 异步读取文件 async with aiofiles.open("recent.txt", "r") as f: recent = int(await f.read()) new_flag = False for val in article_nums: article_num = val.select_one("td", class_='num').get_text() if not article_num.isdecimal(): continue article_num = int(article_num) if article_num > recent: new_flag = True recent = article_num if new_flag: channel = await client.fetch_channel(channel_id) await channel.send(str(recent)) # 异步写入文件 async with aiofiles.open("recent.txt", "w") as f: await f.write(str(recent)) @client.event async def on_ready(): await client.wait_until_ready() scheduler = AsyncIOScheduler() # 调整为你需求的10秒间隔,降低事件循环负载 scheduler.add_job(read_site, 'interval', seconds=10, misfire_grace_time=10) scheduler.start() print(f'We have logged in as {client.user}') client.run(token)
关键修改点说明
- 异步HTTP请求:用
aiohttp替代requests,网络请求过程中事件循环可同时处理Discord心跳等其他任务 - 异步文件IO:用
aiofiles替代同步文件操作,避免阻塞事件循环 - 线程池处理解析:用
asyncio.to_thread将HTML解析操作放到单独线程,隔离CPU密集型操作对事件循环的影响 - 调整调度间隔:将原3秒间隔改为你描述的10秒,减少事件循环的高频负载
额外建议
- 给网络请求和文件操作添加超时处理,避免单个任务长时间占用资源
- 如果目标社区网站提供API,优先使用API获取数据,比网页解析更高效且资源占用更少
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

