Flask异步函数导致Telegram Bot仅能获取单个文件属性的问题
解决Telegram Bot接收文件时的Event loop is closed错误
我用Flask搭建Telegram Bot实现接收客户端文件的功能,首次接收文件时正常,但第二次请求开始触发RuntimeError: Event loop is closed错误,错误发生在file_info = await bot.get_file(file_id)代码行。
原代码
from flask import Flask, request import asyncio import telegram app = Flask(__name__) bot = telegram.Bot(token=TOKEN) # Recieve files from Telegram @app.route('/', methods=['POST']) def handle_request(): data = request.get_json() file_id = data['message']['document']['file_id'] async def get_path (file_id): global file_info file_info = await bot.get_file (file_id) return file_info loop = asyncio.new_event_loop() logging.info(f"loop={loop}") asyncio.run (get_path(file_id)) logging.info (f"file_path={file_info.file_path}") # launch Flask if __name__ == '__main__': app.run(debug=True)
首次执行结果
INFO 83: loop=<UnixSelectorEventLoop running=False closed=False debug=False> INFO 113: file_path=https://api.telegram.org/file/bot557***:***VF0FrPEGq9CnchBHQ4IMzR4oAY/documents/file_28.xlsx
错误日志
Exception on / [POST]: Traceback (most recent call last): File "/home/proj/venv/lib/python3.10/site-packages/telegram/request/_baserequest.py", line 274, in _request_wrapper code, payload = await self.do_request( File "/home/proj/venv/lib/python3.10/site-packages/telegram/request/_httpxrequest.py", line 199, in do_request res = await self._client.request( File "/home/proj/venv/lib/python3.10/site-packages/httpx/_client.py", line 1533, in request return await self.send(request, auth=auth, follow_redirects=follow_redirects) File "/home/proj/venv/lib/python3.10/site-packages/httpx/_client.py", line 1620, in send response = await self._send_handling_auth( File "/home/proj/venv/lib/python3.10/site-packages/httpx/_client.py", line 1648, in _send_handling_auth response = await self._send_handling_redirects( File "/home/proj/venv/lib/python3.10/site-packages/httpx/_client.py", line 1685, in _send_handling_redirects response = await self._send_single_request(request) File "/home/proj/venv/lib/python3.10/site-packages/httpx/_client.py", line 1722, in _send_single_request response = await transport.handle_async_request(request) File "/home/proj/venv/lib/python3.10/site-packages/httpx/_transports/default.py", line 353, in handle_async_request resp = await self._pool.handle_async_request(req) File "/home/proj/venv/lib/python3.10/site-packages/httpcore/_async/connection_pool.py", line 220, in handle_async_request await self._close_expired_connections() File "/home/proj/venv/lib/python3.10/site-packages/httpcore/_async/connection_pool.py", line 188, in _close_expired_connections await connection.aclose() File "/home/proj/venv/lib/python3.10/site-packages/httpcore/_async/connection.py", line 159, in aclose await self._connection.aclose() File "/home/proj/venv/lib/python3.10/site-packages/httpcore/_async/http2.py", line 313, in aclose await self._network_stream.aclose() File "/home/proj/venv/lib/python3.10/site-packages/httpcore/backends/asyncio.py", line 54, in aclose await self._stream.aclose() File "/home/proj/venv/lib/python3.10/site-packages/anyio/streams/tls.py", line 192, in aclose await self.transport_stream.aclose() File "/home/proj/venv/lib/python3.10/site-packages/anyio/_backends/_asyncio.py", line 1323, in aclose self._transport.close() File "/usr/lib/python3.10/asyncio/selector_events.py", line 706, in close self._loop.call_soon(self._call_connection_lost, None) File "/usr/lib/python3.10/asyncio/base_events.py", line 753, in call_soon self._check_closed() File "/usr/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed raise RuntimeError('Event loop is closed') RuntimeError: Event loop is closed
问题根源
asyncio.run()会在协程执行完毕后自动关闭事件循环,而全局的telegram.Bot实例内部的HTTP客户端会复用连接池。第二次请求时,连接池尝试使用已被关闭的事件循环,从而触发报错。
修复方案
方案1:每次请求创建新Bot实例(适合小流量场景)
将Bot实例的创建移到请求处理函数内部,避免复用旧的连接池和事件循环:
from flask import Flask, request import asyncio import telegram import logging app = Flask(__name__) TOKEN = "你的BotToken" @app.route('/', methods=['POST']) def handle_request(): data = request.get_json() file_id = data['message']['document']['file_id'] async def get_path(file_id): # 每次请求创建新的Bot实例 bot = telegram.Bot(token=TOKEN) file_info = await bot.get_file(file_id) return file_info file_info = asyncio.run(get_path(file_id)) logging.info(f"file_path={file_info.file_path}") return "OK" if __name__ == '__main__': logging.basicConfig(level=logging.INFO) app.run(debug=True)
方案2:使用异步Flask(适合高流量场景)
改用flask-asyncio扩展实现异步请求处理,保持Bot实例复用的同时,让事件循环持续运行:
from flask import Flask, request import asyncio import telegram import logging from flask_asyncio import AsyncFlask app = AsyncFlask(__name__) TOKEN = "你的BotToken" # 全局复用Bot实例 bot = telegram.Bot(token=TOKEN) @app.route('/', methods=['POST']) async def handle_request(): data = request.get_json() file_id = data['message']['document']['file_id'] file_info = await bot.get_file(file_id) logging.info(f"file_path={file_info.file_path}") return "OK" if __name__ == '__main__': logging.basicConfig(level=logging.INFO) app.run(debug=True)
注意:方案2需要先安装
flask-asyncio,执行命令:pip install flask-asyncio
内容的提问来源于stack exchange,提问作者Maxim Bratukhin
相关产品推荐
相关产品推荐

