ホームページ  >  記事  >  バックエンド開発  >  PHP のセッション ロック、同時実行性、カバレッジの詳細な説明

PHP のセッション ロック、同時実行性、カバレッジの詳細な説明

小云云
小云云オリジナル
2018-03-22 09:13:351997ブラウズ

この記事では主に、PHP セッションのロックと同時実行に関する現象について説明します。興味のある方は参考にしていただければ幸いです。

ログインできません
ある日、バグを解決するためにバックエンド システムの 1 つにログインしようとしましたが、アカウントのパスワード確認コードを正確に入力したところ、ログインできませんでした。多くの実験を行った結果、主なエラー メッセージが 2 つあることがわかりました:

  • csrf 検証に失敗しました

  • 検証コード エラー [コードの神に誓って、私が使用した検証コードを入力したことを確認します。半角、順序は一貫していました、いいえ さらに文字を追加]

私たちのシステム私たちのシステムは、ご覧のとおり、Palcon 2.0.8に基づいて開発されています。フォームフィールドでの CSRF 攻撃。キャプチャも有効になっています。

<input type="hidden" 
  name="{{ security.getTokenKey() }}"
  value="{{ security.getToken() }}"/>
<img src="/login/getCaptcha" id="img-captcha"/>

最初にこれら 2 つのコンポーネントをチェックしたところ、どちらもセッションにデータを保存していることがわかりました。

# phalcon/security.zep
# Security::getToken()
let session = <SessionInterface> dependencyInjector->getShared("session"); 
session->set(this->_tokenValueSessionID, token); 
$this->session->set(&#39;admin_get_captcha_action&#39;, $captcha);

次に、セッションの実装をチェックしたところ、データが Redis に保存されていることがわかりました。

探して検索 ログインできない問題は何ですか?データの検証に問題があるため、テスト環境の redis マシンにログインし、redis-cli モニターを実行して、ログイン プロセスを実行したところ、出力は次のようになりました。 ):

  • GET sessionId

  • GET sessionId

  • SETEX sessionId 3600 csrf=xxxx

  • SETEX セッション ID 3600 captcha=abcd

次のことがわかります:

1. ここには 2 つのリクエストがあり、1 つはフォームをロードすること、もう 1 つは検証コードを生成することです。
2. 「同時実行」状況が発生します。これらの 2 つのリクエストは、フォームがロードされて表示された後に検証コードを要求する必要があります。つまり、セッション シーケンスは get->set->get->set になります。同時リクエストが行われたように見えますか?
3. 後者の SETEX には csrf コンテンツがありません。つまり、以前のデータが上書きされます。しかし、問題が何であるかは少しわかりました。何が問題なのでしょうか? PHP のセッション データ アクセスから始まる長い話です。

PHP セッション データ アクセス セッション データは文字列にエンコードされ、メモリ [ファイル、データベース、redis、memcache など] に保存されます。セッションを使用するとき、いつメモリにアクセスしますか?データを取得していますか?データはいつメモリに書き込まれますか?

この質問に対する答えは、一部の友人の考えとは異なる場合があります。リクエストでは、PHP は session_start 中に 1 回だけメモリを読み取り、その後、リクエストの終了時に 1 回だけメモリに書き込みます。 session_write_close を呼び出す 時間が来たら、データをメモリにフラッシュして戻し、セッションを閉じます。

そして次の質問が来ます:

1、如果一个会话,同时出现两个读写session请求,没有保证获取1-写入1-获取2-写入2,同时没有cas版本管理机制的情况下,这些并发请求就会彼此读取不到对方的写入,最后写入的会把前面请求写入的session覆盖掉。
2、如果请求是串行的,像登录页面的表单和验证码,也有可能前面的请求已经输出内容了,但是session还没写入,后面的请求就已经发起了。
锁与不锁
解决这种资源的并发一般会通过锁或版本管理来处理。但是版本管理我看不到好的方法。就聊聊锁吧。

其实锁是不大适合,有弊端的。

php的session,默认是用文件存储的,在打开session的时候,会对文件加独占锁,这样,其它请求就无法获取锁了,只能等待直到前面的锁解了。

这样保证了 读取-写入,读取-写入的顺序。

其它存储器,例如mysql,可以借助select for update进行行锁。redis可以通过一个自增键,返回1的获取到锁等来实现。

这个实现的话,对数据流来说很理想,但是,对于目前这种页面大量应用ajax的情况,所有请求排队处理,将大大加大页面展现的耗时,甚至出现请求超时等不可用故障。

没有解决的解决不建议过多使用session,其一次读取一次写入的机制所引发的问题,会造成坑的存在。
在模版渲染前,或请求输出前调用session_write_close

# 立刻回写session,避免session覆盖
$eventManager = $this->view->getEventsManager();
if (!$eventManager) { 
  $eventManager = new Manager();
  $this->view->setEventsManager($eventManager);
}
$eventManager->attach("view:afterRender",function(){
  session_write_close();
});
return $this->view; 
if($login) { 
  # 立刻回写session,避免session读取不到
  $eventManager = $this->dispatcher->getEventsManager();
  if (!$eventManager) {
    $eventManager = new Manager();
    $this->dispatcher->setEventsManager($eventManager);
  }
  $eventManager->attach(&#39;dispatch:afterDispatchLoop&#39;,function(){
    session_write_close();
  });
  return $this->response->setHeader(&#39;Location&#39;, &#39;/&#39;);
}

相关推荐:

php中session锁防止阻塞请求的实例分析

以上がPHP のセッション ロック、同時実行性、カバレッジの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。