[英]Are global variables thread-safe in Flask? How do I share data between requests?
在我的應用中,一個普通的object的state是通過請求改變的,響應依賴於state。
class SomeObj():
def __init__(self, param):
self.param = param
def query(self):
self.param += 1
return self.param
global_obj = SomeObj(0)
@app.route('/')
def home():
flash(global_obj.query())
render_template('index.html')
如果我在我的開發服務器上運行它,我希望得到 1、2、3 等等。 如果同時從 100 個不同的客戶端發出請求,那么 go 會不會出錯? 預期的結果是 100 個不同的客戶端每個看到一個從 1 到 100 的唯一數字。或者會發生這樣的事情:
self.param
增加 1。self.param
再次遞增。由於只有兩個客戶端,預期結果是 1 和 2,而不是 2 和 3。跳過了一個數字。
當我擴展我的應用程序時,這真的會發生嗎? 我應該查看全局變量的哪些替代方案?
您不能使用全局變量來保存此類數據。 它不僅不是線程安全的,也不是進程安全的,而且生產環境中的 WSGI 服務器會產生多個進程。 如果您使用線程來處理請求,您的計數不僅會出錯,而且還會根據處理請求的進程而有所不同。
使用 Flask 之外的數據源來保存全局數據。 數據庫、memcached 或 redis 都是合適的單獨存儲區域,具體取決於您的需要。 如果您需要加載和訪問 Python 數據,請考慮multiprocessing.Manager
。 您還可以將會話用於每個用戶的簡單數據。
開發服務器可以在單線程和進程中運行。 您不會看到您描述的行為,因為每個請求都將被同步處理。 啟用線程或進程,您將看到它。 app.run(threaded=True)
或app.run(processes=10)
。 (在 1.0 中,服務器默認是線程化的。)
一些 WSGI 服務器可能支持 gevent 或其他異步工作者。 全局變量仍然不是線程安全的,因為仍然沒有針對大多數競爭條件的保護。 您仍然可以有一個場景,一個工人獲得一個價值,產出,另一個修改它,產出,然后第一個工人也修改它。
如果您需要在請求期間存儲一些全局數據,您可以使用 Flask 的g
object 。 另一個常見的情況是一些管理數據庫連接的頂級對象。 這種“全局”類型的區別在於它對每個請求都是唯一的,而不是在請求之間使用,並且有一些東西可以管理資源的設置和拆卸。
這並不是對全局線程安全的真正答案。
但我認為在這里提及會議很重要。 您正在尋找一種存儲客戶特定數據的方法。 每個連接都應該以線程安全的方式訪問自己的數據池。
這在服務器端會話中是可能的,它們可以在一個非常簡潔的燒瓶插件中使用: https ://pythonhosted.org/Flask-Session/
如果您設置會話,則session
變量在您的所有路由中都可用,它的行為類似於字典。 存儲在此字典中的數據對於每個連接的客戶端都是單獨的。
這是一個簡短的演示:
from flask import Flask, session
from flask_session import Session
app = Flask(__name__)
# Check Configuration section for more details
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
Session(app)
@app.route('/')
def reset():
session["counter"]=0
return "counter was reset"
@app.route('/inc')
def routeA():
if not "counter" in session:
session["counter"]=0
session["counter"]+=1
return "counter is {}".format(session["counter"])
@app.route('/dec')
def routeB():
if not "counter" in session:
session["counter"] = 0
session["counter"] -= 1
return "counter is {}".format(session["counter"])
if __name__ == '__main__':
app.run()
在pip install Flask-Session
之后,你應該可以運行它了。 嘗試從不同的瀏覽器訪問它,您會發現它們之間沒有共享計數器。
請求外部數據源的另一個示例是緩存,例如Flask-Caching或其他擴展提供的緩存。
common.py
並在其中放置以下內容:from flask_caching import Cache
# Instantiate the cache
cache = Cache()
flask app
的文件中,使用以下代碼注冊緩存:# Import cache
from common import cache
# ...
app = Flask(__name__)
cache.init_app(app=app, config={"CACHE_TYPE": "filesystem",'CACHE_DIR': Path('/tmp')})
# Import cache
from common import cache
# store a value
cache.set("my_value", 1_000_000)
# Get a value
my_value = cache.get("my_value")
雖然完全接受以前的贊成答案,並且不鼓勵將全局變量用於生產和可擴展的 Flask 存儲,但為了原型或非常簡單的服務器,在 Flask '開發服務器'下運行......
...
Python 內置數據類型,我個人使用並測試了全局dict
, 根據 Python 文檔是線程安全的。 過程不安全。
在開發服務器下運行的每個(可能是並發的)Flask 會話中,從這樣的(服務器全局)dict 中插入、查找和讀取都可以。
當這樣的全局 dict 使用唯一的 Flask 會話密鑰作為密鑰時,它對於會話特定數據的服務器端存儲非常有用,否則不適合 cookie(最大大小 4 kB)。
當然,應該小心保護這樣的服務器全局字典,以免變得太大,在內存中。 可以在請求處理期間對某種過期的“舊”鍵/值對進行編碼。
同樣,不建議將其用於生產或可擴展部署,但對於本地面向任務的服務器來說可能沒問題,因為單獨的數據庫對於給定任務來說太多了。
...
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.