我在一个大型多线程 Flask 应用程序中遇到了一个奇怪的问题,我已经成功地在大约十几行内重现了该问题。看来,每当我修改与 Flask 中的路由相关的函数内部的对象属性时,该属性只会在该路由函数的上下文中进行修改,而在任何路由函数之外,它都会保留其先前的值。作为示例,这里我使用 __init__.py 和 paths.py 函数在 /app/ 目录中定义了一个“app”包。
应用程序/__init__.py:
from flask import Flask
class NumberStore:
def __init__(self):
self.number = 1
def set_number(self, number):
self.number = number
app = Flask(__name__)
numberstore = NumberStore()
from app import routes
应用程序/routes.py:
from app import app, numberstore
@app.route('/get')
def get():
return f"The number holder has id {id(numberstore)} and the number is {numberstore.number}"
@app.route('/set')
def set():
numberstore.set_number(2)
return f"The number holder has id {id(numberstore)} and the number has been set to {numberstore.number}"
此外,我有一个方便的脚本,可以在线程中运行服务器:
runserv.py:
import requests
from multiprocessing import Process
from app import app, numberstore
s = Process(target=lambda : app.run())
s.start()
当我在交互式会话中启动服务器并点击“/set”端点时,我期望
numberstore.number
的值更改为 2,对于“/get”和“/set”端点,它确实更改为 2。但是,当我在路由函数的上下文之外访问该对象时,numberstore.number
的值仍为 1。同样,如果我在交互式会话中手动调用 set_number
方法,更改不会传播到路由函数。尽管对象 ID 在所有情况下都保持不变,但这一切都会发生。由于 ID 指的是对象在内存中的实际位置,这对我来说似乎是不可能的。
laptop:~/testserver$ python3 -i runserv.py
* Serving Flask app 'app'
* Debug mode: off
>>> WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
>>> numberstore.number
1
>>> id(numberstore)
139855264957008
>>> print(requests.get("http://127.0.0.1:5000/get").text)
The number holder has id 139855264957008 and the number is 1
>>> print(requests.get("http://127.0.0.1:5000/set").text)
The number holder has id 139855264957008 and the number has been set to 2
>>> numberstore.number
1
>>> print(requests.get("http://127.0.0.1:5000/get").text)
The number holder has id 139855264957008 and the number is 2
>>> numberstore.set_number(3)
>>> numberstore.number
3
>>> print(requests.get("http://127.0.0.1:5000/get").text)
The number holder has id 139855264957008 and the number is 2
在我看来,这是一个功能,而不是一个错误,而且我认为它与 Flask 为每个请求启动的请求会话有关。我的问题是 - 为什么 Flask 会这样做,是否有可能规避它?我的真实服务器中几乎所有的操作都是由数据库更改构成的,这显然不存在这个问题。然而,在一个实例中,我确实需要从路由 Flask 端点函数中修改模块级对象的属性。这可以吗?
弄清楚了 - Flask 分叉了另一个用于路由端点的进程,但我没有发现它。我没有意识到即使在两个单独的地址空间内对象 ID 也会保持相同。
python3 -i runserv.py
* Serving Flask app 'app'
* Debug mode: off
>>> WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
>>> print(requests.get("http://127.0.0.1:5000/get").text)
127.0.0.1 - - [29/Aug/2024 17:18:57] "GET /get HTTP/1.1" 200 -
The number holder has id 139782623142480, the process id is 69246, and the number is 1
>>> import os
>>> os.getpid()
69244