私のアプリでは、リクエストを行うことで一般的なオブジェクトの状態が変更され、応答は状態に依存します。
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の異なるクライアントから同時にリクエストが行われた場合、何か問題が発生する可能性はありますか?予想される結果は、100の異なるクライアントがそれぞれ1〜100の一意の番号を見るということです。または、このようなことが起こります。
self.param
は1ずつ増加します。self.param
は再びインクリメントされます。クライアントは2つしかなかったため、予想される結果は2と3ではなく1と2でした。数はスキップされました。
これは実際にアプリケーションをスケールアップすると起こりますか?グローバル変数の代替案は何ですか?
グローバル変数を使用してこの種のデータを保持することはできません。スレッドセーフではないだけでなく、process安全でもないため、実稼働環境のWSGIサーバーは複数のプロセスを生成します。スレッドを使用してリクエストを処理している場合、カウントが間違っているだけでなく、リクエストを処理したプロセスによってもカウントが異なります。
Flaskの外部のデータソースを使用して、グローバルデータを保持します。データベース、memcached、またはredisはすべて、ニーズに応じて適切な個別のストレージエリアです。 Pythonデータをロードしてアクセスする必要がある場合は、 _multiprocessing.Manager
_ を検討してください。セッションは、ユーザーごとの単純なデータにも使用できます。
開発サーバーは、単一のスレッドとプロセスで実行できます。各リクエストは同期的に処理されるため、説明した動作は表示されません。スレッドまたはプロセスを有効にすると、表示されます。 app.run(threaded=True)
またはapp.run(processes=10)
。 (1.0では、サーバーはデフォルトでスレッド化されています。)
一部のWSGIサーバーは、geventまたは別の非同期ワーカーをサポートする場合があります。大部分の競合状態に対する保護がないため、グローバル変数はまだスレッドセーフではありません。 1人のワーカーが値を取得し、譲り渡し、別のワーカーがそれを修正し、譲り、その後最初のワーカーもそれを修正するというシナリオがあります。
リクエスト中にグローバルデータを保存する必要がある場合、Flaskの g
オブジェクト を使用できます。別の一般的なケースは、データベース接続を管理するトップレベルのオブジェクトです。このタイプの「グローバル」の違いは、それが各リクエストに固有であり、使用されないbetweenリクエストであり、セットアップとティアダウンを管理する何かがあることですリソース。
これは、実際にはグローバルのスレッドセーフに対する答えではありません。
しかし、ここでセッションに言及することが重要だと思います。クライアント固有のデータを保存する方法を探しています。すべての接続は、スレッドセーフな方法で、独自のデータプールにアクセスできる必要があります。
これはサーバー側のセッションで可能であり、非常にきれいなflask=プラグイン: 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 __== '__main__':
app.run()
pip install Flask-Session
の後、これを実行できるはずです。異なるブラウザーからアクセスしてみてください。カウンターがそれらの間で共有されていないことがわかります。