部署Heroku后Python Telegram Bot的ConversationHandler工作异常
问题根因
这个问题的核心是ConversationHandler默认将会话状态存储在当前进程的内存中,部署到Heroku后触发了进程内存不共享的问题:
- 本地测试时是单进程运行,会话状态始终存在当前进程内存中,所以流程正常
- Heroku上部署WSGI应用(如Flask+Gunicorn)默认会启动多个worker进程,每个进程的
Dispatcher实例独立,内存数据不互通。用户前序请求落到A进程,状态存在A的内存中,后续请求落到B进程时,B进程没有对应会话数据,就会出现日志中的state None无响应情况。双击按钮成功是刚好这次请求落到了持有会话状态的进程中。
另外还有次要触发原因:Telegram服务器如果10秒内没有收到webhook请求的响应,会重试推送同一条更新,也会导致同一条更新被多次处理,出现多条重复日志。
解决方法
临时验证方案(快速确认问题)
修改Heroku的Procfile启动命令,将Gunicorn的worker数改为1,强制所有请求都落到同一个进程,会话状态可以正常读取:
web: gunicorn --workers=1 你的启动文件名:app
这个方案的缺点是单进程性能有限,且Heroku进程重启后所有会话状态都会丢失,仅适合验证问题。
永久解决方案
1. 配置会话持久化
使用Python Telegram Bot内置的持久化机制,将会话状态存储到所有进程都能访问的外部存储,避免内存不共享问题:
- 如果你能接入Redis,可以直接使用
RedisPersistence - 如果你已使用数据库存储业务数据,可以自己实现基于数据库的
Persistence子类 - 临时测试也可以用
PicklePersistence,但注意Heroku临时文件系统重启会丢失数据
示例配置(以v13.x版本为例):
from telegram.ext import PicklePersistence, Dispatcher def setup(bot, db_pool): # 初始化持久化实例 persistence = PicklePersistence(filename='conv_states') # 创建dispatcher时传入持久化参数 dispatcher = Dispatcher(bot, None, workers=0, persistence=persistence) # 后续注册handler逻辑不变 ...
2. 增加更新去重逻辑
在webhook入口处增加update_id去重,避免Telegram重试的重复更新被多次处理:
# 可以用redis或者数据库存储已处理的update_id processed_updates = set() @app.route(f"/{TOKEN}", methods=["POST"]) def respond(): update_json = request.get_json(force=True) update_id = update_json.get('update_id') if update_id in processed_updates: return "ok" processed_updates.add(update_id) update = telegram.Update.de_json(update_json, bot) dispatcher.process_update(update) return "ok"
3. 优化回调处理速度
确保所有回调处理逻辑在10秒内完成返回,避免Telegram触发重试,耗时操作可以放到后台异步处理。
内容的提问来源于stack exchange,提问作者Palash Singh Raghuwanshi
相关产品推荐
相关产品推荐

