亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

php session的鎖和并發分別是什么

發布時間:2021-07-02 15:28:12 來源:億速云 閱讀:128 作者:chen 欄目:開發技術

本篇內容主要講解“php session的鎖和并發分別是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“php session的鎖和并發分別是什么”吧!

我登錄不了了
某天,我準備登錄我們一個后臺系統,前去解決一個bug,在賬戶密碼驗證碼都準確輸入的情況下,我登錄不上,經過多次實驗發現主要有兩個錯誤信息:

  • csrf驗證失敗

  • 驗證碼錯誤【我對碼神起誓我用半角輸入了我看到的驗證碼,且順序一致,無多加字符】

我們的系統
我們的系統是基于phalcon 2.0.8 開發的,如你所見,我們在表單域加入了防止csrf攻擊的域。也啟用了驗證碼。

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

我首先對這兩個組件進行查閱,發現他們都是將數據存于session:

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

然后我又查閱了我們session的實現,發現是將數據存儲于redis的。

找啊找
什么問題導致我登錄不上呢?既然是數據驗證上出現問題,就從數據著手吧,我登陸我們測試環境的redis機器,執行 redis-cli monitor,然后走一遍登錄流程,發現輸出如下(意思意思):

  • GET sessionId 
     

  • GET sessionId 
     

  • SETEX sessionId 3600 csrf=xxxx 
     

  • SETEX sessionId 3600 captcha=abcd 
     

我們可以看到:

1、這里存在兩次請求,一次是表單加載,一次是生成驗證碼的。
2、存在“并發”的情況,這兩個請求應該是表單加載渲染后才請求驗證碼的,也就是session順序應該是get->set->get->set,看起來怎么是并發請求了。
3、后面那個SETEX沒有csrf的內容,也就是覆蓋掉前面的數據了
整個世界都不好了,不過也稍微明白是什么問題了。什么問題呢,說來話長,要從PHP的session數據的存取說起。

php的session數據的存取
session的數據是經過編碼成字符串存儲在存儲器【file、db、redis、memcache等】的,在我們使用session的時候,是什么時候去儲存器取數據的?又是什么時候將數據寫入存儲器的?

這個問題的答案可能和一些朋友想的不一樣,一個請求里面,PHP只會讀取一次存儲器,在session_start的時候,然后也只會寫入一次存儲器,在請求結束的時候,或調用session_write_close的時候,將數據刷回存儲器,關閉session。

那么問題來了:

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('dispatch:afterDispatchLoop',function(){
    session_write_close();
  });
  return $this->response->setHeader('Location', '/');
}

到此,相信大家對“php session的鎖和并發分別是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

平利县| 会东县| 沙坪坝区| 东乡| 中宁县| 达州市| 东莞市| 东山县| 清河县| 丹凤县| 阿拉善右旗| 关岭| 长乐市| 平山县| 沾化县| 炉霍县| 武义县| 平舆县| 镇雄县| 华阴市| 乌兰察布市| 醴陵市| 舟曲县| 聂拉木县| 博客| 隆德县| 邯郸市| 陕西省| 巴中市| 临泉县| 武清区| 会泽县| 海口市| 含山县| 双城市| 阿克陶县| 南江县| 桐庐县| 南木林县| 通山县| 佛坪县|