部署在Heroku的Telegram Bot使用Webhook时定时任务失效问题
问题解决:Webhook模式下Telegram Bot定时任务停止运行的修复方案
核心原因
使用start_webhook时,它会自行管理异步事件循环,你之前在__main__中创建的check任务没有被正确关联到Webhook启动的事件循环中,导致任务执行到第一个await asyncio.sleep后就被挂起,无法继续循环。
修复步骤
1. 将定时任务移至on_startup回调中
Webhook启动时会触发on_startup函数,此时事件循环已完全初始化,在这里创建定时任务才能被正确纳入循环管理。同时要保存任务引用,避免被垃圾回收:
import asyncio from aiogram import Dispatcher # 全局变量保存定时任务引用 check_task = None async def on_startup(dp: Dispatcher): global check_task # 启动每分钟检查的定时任务 check_task = asyncio.create_task(check(60)) # 保留你原有的Webhook初始化逻辑(比如设置Telegram Webhook地址) await setup_webhook(dp) async def on_shutdown(dp: Dispatcher): global check_task # 关闭定时任务 if check_task: check_task.cancel() try: await check_task except asyncio.CancelledError: pass # 保留你原有的关闭逻辑 await dp.storage.close() await dp.storage.wait_closed()
2. 优化check函数,避免阻塞事件循环
如果你的parsing()是同步函数,必须用run_in_executor包装,防止阻塞Webhook的事件循环(Webhook需要实时响应Telegram的请求,阻塞会导致服务异常):
async def check(wait_for): print("Debug: check is awaited") while True: print("Debug: inside while") await asyncio.sleep(wait_for) print("after sleep") # 用线程池执行同步的parsing函数 loop = asyncio.get_running_loop() transactions = await loop.run_in_executor(None, parsing) # 这里添加交易处理逻辑(比如发送Telegram消息)
3. 启动Webhook的代码保持不变
start_webhook( dispatcher=dp, webhook_path=WEBHOOK_PATH, on_startup=on_startup, on_shutdown=on_shutdown, skip_updates=True, host=WEBAPP_HOST, port=WEBAPP_PORT )
额外注意事项
- Heroku的Web服务有请求超时限制,确保所有异步操作都不会长时间阻塞事件循环。
- 如果
parsing()涉及网络请求,建议改用异步请求库(如aiohttp)替代同步库,进一步提升性能。
内容的提问来源于stack exchange,提问作者Valerii
相关产品推荐
相关产品推荐

