我在一个大型多线程 Flask 应用程序中遇到了一个奇怪的问题,我设法用大约十几行代码重现了这个问题。似乎每当我修改内部对象的属性时...
我在一个大型多线程 Flask 应用程序中遇到了一个奇怪的问题,我设法用大约十几行代码重现了这个问题。似乎每当我修改与 Flask 中的路由绑定的函数内对象的属性时,该属性只会在该路由函数的上下文中被修改,而在任何路由函数之外,它都保留其先前的值。作为示例,我在这里使用 __init__.py 和 routes.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}"
此外,我还有一个便捷脚本,可以在线程中运行服务器:
运行服务.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\' 端点。但是,当我在路由函数上下文之外访问对象时, 的值 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