Flask搭配异步Firestore客户端二次加载页面时出现Event loop is closed错误
Flask + Async Firestore 客户端:解决"Event loop is closed"错误
问题现象
运行结合firestore_async的Flask应用时,首次加载页面正常,第二次加载触发Event loop is closed运行时错误;使用WSGI worker时错误偶现;尝试为每个请求创建新客户端也无法解决。
示例复现代码:
import firebase_admin from firebase_admin import credentials, firestore_async from flask import Flask cred = credentials.Certificate('credentials.json') firebase_admin.initialize_app(cred) db = firestore_async.client() app = Flask(__name__) @app.route('/') async def hello_world(): await db.collection("product").get() return 'Hello, World!' if __name__ == "__main__": app.run(debug=True)
问题根源
- Flask debug模式的重载器:Werkzeug开发服务器在debug模式下默认启动进程重载器,重载后原进程的事件循环被关闭,但Firestore异步客户端会缓存旧的事件循环引用,导致后续请求调用已关闭的循环。
- Flask原生运行方式的限制:
app.run()处理async视图时会创建临时事件循环,但Firestore客户端的内部逻辑会绑定全局循环,循环被回收后无法自动重建绑定。
解决方案
1. 关闭debug模式的重载器(开发环境快速修复)
修改启动代码,禁用进程重载器,避免事件循环被重复关闭:
if __name__ == "__main__": app.run(debug=True, use_reloader=False)
2. 使用ASGI服务器(生产/稳定开发环境推荐)
Flask的async视图需要ASGI服务器来正确管理事件循环,推荐使用hypercorn或uvicorn:
- 先安装依赖:
pip install hypercorn - 修改启动逻辑,用ASGI方式运行:
import firebase_admin from firebase_admin import credentials, firestore_async from flask import Flask from hypercorn.asyncio import serve from hypercorn.config import Config import asyncio cred = credentials.Certificate('credentials.json') firebase_admin.initialize_app(cred) db = firestore_async.client() app = Flask(__name__) @app.route('/') async def hello_world(): await db.collection("product").get() return 'Hello, World!' if __name__ == "__main__": config = Config() config.bind = ["0.0.0.0:5000"] config.debug = True asyncio.run(serve(app, config))
3. 为每个请求绑定活跃事件循环(保留重载器的场景)
通过Flask的请求钩子,在每次请求前确保Firestore客户端绑定当前活跃的事件循环:
import firebase_admin from firebase_admin import credentials, firestore_async from flask import Flask, g import asyncio cred = credentials.Certificate('credentials.json') firebase_admin.initialize_app(cred) app = Flask(__name__) @app.before_request def setup_firestore(): # 为每个请求创建新的客户端,绑定当前请求的事件循环 g.db = firestore_async.client() @app.route('/') async def hello_world(): await g.db.collection("product").get() return 'Hello, World!' if __name__ == "__main__": app.run(debug=True)
注意:firebase_admin.initialize_app只能执行一次,不要放在请求钩子中。
内容的提问来源于stack exchange,提问作者lennertcl
相关产品推荐
相关产品推荐

