この記事では、PHP セッションの使用中のロックと同時実行性の問題について説明します。関連する現象には、リクエストのブロック、セッション データの損失、セッション データの読み取り不可などがあります。
ログインできません
ある日、バグを解決するためにバックエンド システムの 1 つにログインしようとしました。アカウントとパスワードの確認コードを正確に入力したにもかかわらず、ログインできませんでした。多くの実験の結果、主に 2 つの原因があることがわかりました。エラーメッセージ:
私たちのシステム
私たちのシステムは falcon 2.0.8 に基づいて開発されており、フォームフィールドに CSRF 攻撃を防ぐためのフィールドが追加されています。キャプチャも有効になっています。
最初にこれら 2 つのコンポーネントを確認したところ、どちらもセッションにデータを保存していることがわかりました。 リーリー
その後、セッションの実装を確認したところ、データが Redis に保存されていることがわかりました。
見て見て
ログインできない問題は何ですか?データの検証に問題があるため、テスト環境の redis マシンにログインし、redis-cli モニターを実行して、ログイン プロセスを実行したところ、出力は次のようになりました。 ):
1. ここには 2 つのリクエストがあります。1 つはフォームのロードで、もう 1 つは検証コードの生成です。
2. 「同時実行」状況が存在します。これらの 2 つのリクエストは、フォームがロードされて表示された後に検証コードを要求する必要があります。つまり、セッション シーケンスは get->set->get->set である必要があります。同時リクエストのようです?
3. 後者の SETEX には csrf コンテンツがありません。つまり、以前のデータが上書きされます
世界全体が良いことではありませんが、何が問題なのか少しは理解できました。何が問題なのでしょうか? PHP のセッション データ アクセスから始まる長い話です。
PHPセッションデータへのアクセス
セッションデータは文字列にエンコードされ、ストレージ[ファイル、データベース、redis、memcacheなど]に保存されます。セッションを使用するとき、いつストレージからデータを取得しますか?データはいつメモリに書き込まれますか?
そして次の質問が来ます:
1. セッション内に同時に 2 つの読み取りおよび書き込みセッション リクエストがある場合、get 1-write 1-get 2-write 2 の保証はなく、cas バージョン管理メカニズムがないため、これらの同時リクエストは相手が書き込まないと、最後の書き込みによって、前のリクエストによって書き込まれたセッションが上書きされます。
2. ログイン ページのフォームや確認コードなど、リクエストがシリアルである場合、前のリクエストでコンテンツがすでに出力されているものの、セッションがまだ書き込まれておらず、後続のリクエストが開始されている可能性があります。
ロックまたはロック解除
このようなリソースの同時実行性の解決は、通常、ロックまたはバージョン管理を通じて処理されます。しかし、バージョン管理を行う良い方法がわかりません。ロックについて話しましょう。
PHP のセッションはデフォルトでファイルに保存され、セッションが開かれると、そのファイルに排他的ロックが追加され、他のリクエストはロックを取得できなくなり、前のロックが解放されるまで待つことができます。
これにより、読み取り-書き込み、読み取り-書き込みの順序が保証されます。
mysql などの他のストレージは、select for update を使用して行ロックを実行できます。 Redis は、自動インクリメント キーを介して実装でき、1 を返してロックを取得するなどします。
この実装はデータ フローには理想的ですが、Ajax がページで広く使用されている現在の状況では、すべてのリクエストが処理のためにキューに入れられるため、ページ表示の時間が大幅に増加し、リクエストなどの利用不能エラーが発生することもあります。タイムアウト。
解決策はありません
セッションを過度に使用することはお勧めできません。そのリードワンスライトワンスメカニズムによって引き起こされる問題は、落とし穴を引き起こす可能性があります。
テンプレートをレンダリングする前、または出力をリクエストする前に session_write_close を呼び出します
リーリー
リーリー