Docker环境多Telegram Bot启动报错,求线程/异步运行解决方案
Docker中运行多Telegram Bot报错及线程模式运行解决方案
问题描述
本地Windows/Mac环境可正常启动大量Telegram Bot,但Docker容器中运行时触发以下报错:
backend1-1 | File "uvloop/loop.pyx", line 2887, in uvloop.loop.Loop.add_signal_handler
backend1-1 | ValueError: add_signal_handler() can only be called from the main thread
核心相关代码:
class BaseTelegramBot: """Telegram Bot基类""" def __init__(self, bot_data): super().__init__() self.bot = Bot(token=self.token) self.dispatcher = Dispatcher() bot = BaseTelegramBot(bot_data=bot_data) asyncio.run(bot.start()) async def start(self): py_logger.info(f"Bot {self.bot_data.id} 已启动") await self.bot.set_my_commands(commands=self.commands) await self.dispatcher.start_polling(self.bot)
报错原因
Docker环境下,Python asyncio默认使用uvloop作为事件循环(Linux环境自动启用),而start_polling内部会尝试添加信号处理器,uvloop严格要求该操作必须在主线程执行。本地环境可能使用默认的SelectorEventLoop,对线程限制宽松;或者本地代码在主线程执行,而Docker中因启动逻辑导致代码运行在非主线程,从而触发报错。
解决方案
方案1:线程模式批量启动Bot
通过线程池为每个Bot创建独立的事件循环,确保每个Bot的信号处理器操作在所属线程的主线程执行:
import asyncio from concurrent.futures import ThreadPoolExecutor class BaseTelegramBot: """Telegram Bot基类""" def __init__(self, bot_data): super().__init__() self.bot = Bot(token=self.token) self.dispatcher = Dispatcher() self.bot_data = bot_data async def start(self): py_logger.info(f"Bot {self.bot_data.id} 已启动") await self.bot.set_my_commands(commands=self.commands) # 启动轮询,可根据库文档调整参数避免自动添加信号处理器 await self.dispatcher.start_polling(self.bot, skip_updates=True) def run_bot_thread(bot_data): # 为当前线程创建并设置独立事件循环 loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) bot = BaseTelegramBot(bot_data=bot_data) try: loop.run_until_complete(bot.start()) finally: loop.close() # 批量启动逻辑 if __name__ == "__main__": bot_configs = [...] # 替换为你的所有Bot配置列表 # 根据机器性能设置最大线程数 with ThreadPoolExecutor(max_workers=5) as executor: executor.map(run_bot_thread, bot_configs)
方案2:强制使用默认事件循环
如果不想修改线程逻辑,可强制禁用uvloop,使用Python默认的SelectorEventLoop规避线程限制:
import asyncio import sys # 在代码启动前设置事件循环策略 if sys.platform != 'win32': asyncio.set_event_loop_policy(asyncio.DefaultEventLoopPolicy()) # 原启动代码 bot = BaseTelegramBot(bot_data=bot_data) asyncio.run(bot.start())
内容的提问来源于stack exchange,提问作者Nikki Nikkonor
相关产品推荐
相关产品推荐

