很多旧教程里的 @app.before_first_request 在新版本里报错。要实现「第一次请求前做一次」应该怎么写?并发和多进程下会不会重复执行?这篇用真实的 gunicorn 并发请求一一数出来。
Flask 版本 3.1.3 | hasattr(app, "before_first_request") = False 用 @app.before_first_request → AttributeError: 'Flask' object has no attribute 'before_first_request' Flask 类上还留着的 before_ 开头的方法: ['before_request']
上一篇:before_request、after_request、teardown_request;下一篇:异常处理器的匹配顺序。
源码:
1 | def create_app():
2 | app = Flask(__name__)
3 | with app.app_context():
4 | warm_up() # 加载配置、建表、预热缓存……
5 | return app
create_app() 耗时 61 ms(包含 50 ms 的 warm_up);之后 5 个请求,warm_up 总共执行了 1 次 app.py 中两种写法(省略 init 的定义):
1 | def init(tag):
2 | time.sleep(0.2)
3 | with open(LOG, "a") as f: f.write(f"{tag} pid={os.getpid()}\n")
4 | if os.environ["MODE"] == "nolock":
5 | @app.before_request
6 | def first():
7 | global _done
8 | if not _done:
9 | init("nolock"); _done = True
10 | elif os.environ["MODE"] == "lock":
11 | @app.before_request
12 | def first():
13 | global _done
14 | if not _done:
15 | with _lock:
16 | if not _done:
17 | init("lock"); _done = True
18 | elif os.environ["MODE"] == "module":
19 | init("module-level")
MODE=nolock 1 个 worker、16 线程,12 个并发的第一批请求:init 执行了 12 次(都返回 200: True)
MODE=lock 1 个 worker、16 线程,12 个并发的第一批请求:init 执行了 1 次(都返回 200: True)
MODE=module 1 个 worker、16 线程,12 个并发的第一批请求:init 执行了 1 次(都返回 200: True)--workers 3 MODE=module:init 执行 3 次,涉及 3 个不同的 pid --workers 3 --preload MODE=module:init 执行 1 次,涉及 1 个不同的 pid --workers 3 MODE=lock: init 执行 3 次,涉及 3 个不同的 pid(线程锁只在进程内有效)