为何基于Webhooks的Python Telegram Bot无法像Long Polling那样并发处理多用户消息?
我用aiogram框架开发Telegram Bot,测试逻辑是接收用户消息后10秒返回回声消息。实际要做的是服务大量用户的电影购买Bot,要求支持多用户消息并发处理,且必须用Webhooks方式接收消息。
下面是两段测试用的Python脚本:
基于Long Polling的Telegram Bot
import asyncio import logging from aiogram import Bot, Dispatcher, executor, types from bot_files.config import * # Configure logging logging.basicConfig(level=logging.INFO) # Initialize bot and dispatcher bot = Bot(token=bot_token) dp = Dispatcher(bot) @dp.message_handler() async def echo(message: types.Message): await asyncio.sleep(10) await message.answer(message.text) if __name__ == '__main__': executor.start_polling(dp, skip_updates=True)
基于Webhooks的Telegram Bot
import asyncio import logging from aiogram import Bot, Dispatcher, executor, types from bot_files.config import * # Configure logging logging.basicConfig(level=logging.INFO) # Initialize bot and dispatcher bot = Bot(token=bot_token) dp = Dispatcher(bot) WEBHOOK_HOST = f'https://7417-176-8-60-184.ngrok.io' WEBHOOK_PATH = f'/webhook/{bot_token}' WEBHOOK_URL = f'{WEBHOOK_HOST}{WEBHOOK_PATH}' # webserver settings WEBAPP_HOST = '0.0.0.0' WEBAPP_PORT = os.getenv('PORT', default=5000) async def on_startup(dispatcher): await bot.set_webhook(WEBHOOK_URL, drop_pending_updates=True) async def on_shutdown(dispatcher): await bot.delete_webhook() @dp.message_handler() async def echo(message: types.Message): await asyncio.sleep(10) await message.answer(message.text) if __name__ == '__main__': executor.start_webhook( dispatcher=dp, webhook_path=WEBHOOK_PATH, skip_updates=True, on_startup=on_startup, on_shutdown=on_shutdown, host=WEBAPP_HOST, port=WEBAPP_PORT )
测试发现:Long Polling模式下,两名用户同时发消息时能并发处理,都在10秒内收到回复;但Webhooks模式下消息会线性处理,其中一名用户要等20秒才能收到回复。请问为什么基于Webhooks的Telegram Bot无法像Long Polling那样同时处理多用户消息?
问题原因
默认情况下,aiogram的executor.start_webhook()使用单线程的aiohttp服务器,每个请求会被串行处理——当第一个请求的处理函数(比如你的echo)里有await asyncio.sleep(10)这类耗时异步操作时,服务器会被该请求占用,直到处理完成才会响应下一个请求。
而Long Polling模式的executor.start_polling()内部是通过异步任务拉取和处理消息,每条新消息都会被包装成独立的异步任务交给事件循环并发执行,所以多用户请求能同时处理。
解决方法
要让Webhook模式支持并发,需要修改Web服务器配置,启用多线程/多进程处理请求,同时确保处理逻辑是真正的异步:
1. 启用多线程Web服务器
调用executor.start_webhook()时添加workers参数指定工作线程数,比如设置为4:
if __name__ == '__main__': executor.start_webhook( dispatcher=dp, webhook_path=WEBHOOK_PATH, skip_updates=True, on_startup=on_startup, on_shutdown=on_shutdown, host=WEBAPP_HOST, port=WEBAPP_PORT, workers=4 # 指定工作线程数,实现并发处理 )
workers参数会让aiohttp启动多个线程,每个线程拥有独立的事件循环,多个请求就能并行处理。
2. 确保处理逻辑异步无阻塞
如果后续的电影购买逻辑包含同步阻塞操作(比如数据库查询、文件IO),要将其包装为异步任务,避免阻塞事件循环。例如:
@dp.message_handler() async def buy_movie(message: types.Message): # 把同步操作放到独立线程执行,不阻塞事件循环 result = await asyncio.to_thread(sync_db_query, message.text) await message.answer(f"购买结果:{result}")
内容的提问来源于stack exchange,提问作者Tomas Angelo

