《PHP編程:php session的鎖和并發》要點:
本文介紹了PHP編程:php session的鎖和并發,希望對您有用。如果有疑問,可以聯系我們。
本文分享PHP的session在使用過程中的鎖和并發的問題,與之相關的現象有哀求阻塞、session數據丟失、session數據讀不到.PHP應用
我登錄不了了
某天,我準備登錄我們一個后臺系統,前去解決一個bug,在賬戶暗碼驗證碼都準確輸入的情況下,我登錄不上,經過多次實驗發現主要有兩個錯誤信息:PHP應用
我們的系統
我們的系統是基于phalcon 2.0.8 開發的,如你所見,我們在表單域加入了防止csrf攻擊的域.也啟用了驗證碼.PHP應用
<input type="hidden" name="{{ security.getTokenKey() }}" value="{{ security.getToken() }}"/> <img src="/login/getCaptcha" id="img-captcha"/>
我首先對這兩個組件進行查閱,發現他們都是將數據存于session:PHP應用
# 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的.PHP應用
找啊找
什么問題導致我登錄不上呢?既然是數據驗證上出現問題,就從數據著手吧,我登陸我們測試環境的redis機器,執行 redis-cli monitor,然后走一遍登錄流程,發現輸出如下(意思意思):PHP應用
我們可以看到:PHP應用
1、這里存在兩次哀求,一次是表單加載,一次是生成驗證碼的.
2、存在“并發”的情況,這兩個哀求應該是表單加載渲染后才哀求驗證碼的,也就是session順序應該是get->set->get->set,看起來怎么是并發哀求了.
3、后面那個SETEX沒有csrf的內容,也就是覆蓋掉前面的數據了
整個世界都不好了,不過也稍微明白是什么問題了.什么問題呢,說來話長,要從PHP的session數據的存取說起.PHP應用
php的session數據的存取
session的數據是經過編碼成字符串存儲在存儲器【file、db、redis、memcache等】的,在我們使用session的時候,是什么時候去儲存器取數據的?又是什么時候將數據寫入存儲器的?PHP應用
這個問題的答案可能和一些朋友想的不一樣,一個哀求里面,PHP只會讀取一次存儲器,在session_start的時候,然后也只會寫入一次存儲器,在哀求結束的時候,或調用session_write_close的時候,將數據刷回存儲器,關閉session.PHP應用
那么問題來了:PHP應用
1、如果一個會話,同時出現兩個讀寫session哀求,沒有保證獲取1-寫入1-獲取2-寫入2,同時沒有cas版本管理機制的情況下,這些并發哀求就會彼此讀取不到對方的寫入,最后寫入的會把前面哀求寫入的session覆蓋掉.
2、如果哀求是串行的,像登錄頁面的表單和驗證碼,也有可能前面的哀求已經輸出內容了,但是session還沒寫入,后面的哀求就已經發起了.
鎖與不鎖
解決這種資源的并發一般會通過鎖或版本管理來處理.但是版本管理我看不到好的方法.就聊聊鎖吧.PHP應用
其實鎖是不大適合,有弊端的.PHP應用
php的session,默認是用文件存儲的,在打開session的時候,會對文件加獨占鎖,這樣,其它哀求就無法獲取鎖了,只能等待直到前面的鎖解了.PHP應用
這樣保證了 讀取-寫入,讀取-寫入的順序.PHP應用
其它存儲器,例如mysql,可以借助select for update進行行鎖.redis可以通過一個自增鍵,返回1的獲取到鎖等來實現.PHP應用
這個實現的話,對數據流來說很理想,但是,對于目前這種頁面大量應用ajax的情況,所有哀求排隊處理,將大大加大頁面展現的耗時,甚至出現哀求超時等不可用故障.PHP應用
沒有解決的解決
不建議過多使用session,其一次讀取一次寫入的機制所引發的問題,會造成坑的存在.
在模版渲染前,或哀求輸出前調用session_write_close
PHP應用
# 立刻回寫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的鎖和并發,希望對大家的學習有所贊助.PHP應用
維易PHP培訓學院每天發布《PHP編程:php session的鎖和并發》等實戰技能,PHP、MYSQL、LINUX、APP、JS,CSS全面培養人才。