如何解决Heroku启动时出现的R12(Exit timeout)错误?
问题根因
你触发Heroku R12错误的核心原因有两个:
- 用了同步阻塞的
time.sleep(),会直接卡住整个异步事件循环,且你直接await cool_down_ended(message)会导致on_message事件被阻塞24分钟才能返回 - 进程收到Heroku发送的SIGTERM重启信号时,没有对应处理逻辑退出挂起的延时任务,超过30秒无法正常退出就会被强制杀进程,返回状态码137
简便修复方案
第一步:替换阻塞sleep,把延时任务改为后台运行
不要直接await延时函数,用asyncio.create_task将其放到后台执行,同时把time.sleep换成异步非阻塞的asyncio.sleep,避免卡住事件循环:
import asyncio # 用来存储所有挂起的延时任务,后续处理进程退出时会用到 pending_tasks = set() @client.listen() async def on_message(message): """ 检测用户发送的'bhwagon'指令,24分钟后给用户发提醒DM :param message: 用户发送的消息 :return: 给用户发送的冷却结束提醒私信 """ channel = client.get_channel(int(WAGON_CHANNEL)) if message.content.startswith("bhwagon"): channel = message.channel # 将延时任务放到后台执行,不阻塞当前事件响应 task = asyncio.create_task(cool_down_ended(message)) pending_tasks.add(task) # 任务执行完成后自动从待处理集合中移除 task.add_done_callback(pending_tasks.discard) async def cool_down_ended(message): """ 用户发送'bhwagon'指令24分钟后,给发送者发私信提醒冷却结束 :param message: 用户发送的原始消息 :return: 给用户发送的提醒私信 """ # 替换为异步非阻塞sleep,不会卡住整个事件循环 await asyncio.sleep(1440) # 24分钟 = 1440秒 await message.author.send("你的偷车冷却已经结束了🎩 可以再去刷一波材料了!")
第二步:添加SIGTERM信号处理逻辑,收到终止信号时主动清退任务
注册信号处理逻辑,当Heroku发送SIGTERM要求重启进程时,主动取消所有挂起的延时任务,让进程在30秒内正常退出:
import signal def handle_sigterm(*args): # 取消所有还在等待的延时任务 for task in pending_tasks: task.cancel() # 正常关闭机器人客户端 asyncio.create_task(client.close()) # 注册SIGTERM信号的处理函数 signal.signal(signal.SIGTERM, handle_sigterm)
修改完成后重新部署即可解决R12报错问题。
内容的提问来源于stack exchange,提问作者yet-it-compiles
相关产品推荐
相关产品推荐

