Web提速:避免php session拖慢執行速度
Web提速:避免php session拖慢運行速度
?
一、WHAT--並發訪問,堵塞運行
1.1?不使用session
文件index.php:
<script type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script> <script type="text/javascript"> $(document).ready(function(){ $.ajax({url:"/ajax.php"}); $.ajax({url:"/ajax2.php"}); $.ajax({url:"/ajax3.php"}); }); </script>
文件ajax.php、ajax2.php、ajax3.php的內容都是
<?php
sleep(1);
echo "php script run";
每一個請求都sleep?1s,jq的ajax請求是異步的,也就是說這三個請求基本上是同一時候發出的,理論上最好的情況是瀏覽器等待1s,3個接口所有返回。
訪問http://localhost,在chrome下查看測試結果:
圖?1.1?不使用session測試結果
測試結果基本上跟理論一致
1.2?使用session
如今我們把文件ajax.php、ajax2.php、ajax3.php都改為
<?
php session_start(); sleep(1); echo "php script run";
這樣做會什麽有什麽差別嗎?我們直接看測試結果:
圖?1.2?使用session測試結果
不是每一個請求僅僅運行1s鐘嗎?為什麽ajax2.php消耗了2s。ajax3.php消耗3s?
二、WHO?--?Session鎖
session鎖的官方定義:
????????Session?data?is?usually?stored?after?your?script?terminated?without?the?need?to?call?session_write_close(),?but?as?session?data?is?locked?to?prevent?concurrent?writes?only?one?script?may?operate?on?a?session?at?any?time.
大致的意思是:?從調用session_start()開始,直到顯示調用session_write_close()或腳本結束,session的數據都會被鎖起來,屆時同一個SESSIONID用戶的請求會被堵塞。
從圖1.2的測試結果看到,ajax2.php與ajax3.php分別多運行了1s和2s。很明顯session_start()操作造成了堵塞。
當index.php同一時候訪問ajax.php、ajax2.php、ajax3.php,ajax.php首先運行。此時ajax2.php與ajax3.php都在等在ajax.php釋放session鎖。都消耗1s。
當ajax.php運行完畢。釋放了session鎖,ajax2.php與ajax3.php再次競爭session鎖,同理ajax3.php又等待了1s鐘。
所以我們得到的結果:
ajax.php?消耗1s
ajax2.php?消耗2s
ajax3.php?消耗3s
三、WHEN?--?什麽時候會觸發Session鎖
在章節二中,session鎖定義為session_start()開始即會觸發session鎖,所以對於現有大部分php框架(使用原生php?session的情況下)存在session鎖造成的用戶請求堵塞的問題。試想,有2個請求,當中請求A須要3s鐘才幹返回結果,另外請求B僅須要10ms即能返回,前端同一時候請求這兩個接口,假如後臺先處理了A請求,那麽B請求就要等待3s後再運行10ms才幹返回結果。可是最優的情況是,同一時候發起請求,10ms後收到B請求返回,3s後收到A請求返回。
四、WHY?--?Session內部運行機制
默認情況下php?session採用文件為服務端存儲介質。在php?session模塊的源代碼中。有一個比較重要的結構體:
圖4.1?php?session模塊結構體?ps_module_struct
該結構體裏面的幾個函數指針分別相應了session操作的open、close、read、write、destory、gc回收等功能。看到這6個函數,是否想起了php的SessionHandlerInterface接口(版本號>=php5.4)?
圖4.2?php?SessionHandlerInterface接口
用這個接口配合session_set_save_handler能夠重寫session的這幾個關鍵操作。於是我們能夠使用下面代碼來探探php?session在一個請求的生命周期中的運行順序:
圖4.3?session運行順序測試代碼
測試結果:
由測試結果可知一般的session運行順序。在session_start()調用時,php回去調用open和read操作,腳本運行結束後(輸出了php?script?run),再會調用write和close操作。
如今我們最好還是做一個大膽的猜想。php?在?session的open或者read操作時,開啟了session鎖。並write或close後釋放session鎖。這樣的猜想也符合我們在章節1的測試結果。
為了驗證我們的猜想。就須要去php的源代碼探個到底了。在php源代碼文件/?ext?/?session?/?mod_files.c中能夠看到默認session的6個重要操作的部分實現。在156行(點擊打開鏈接)。看到open操作有一個打開文件操作:flock(data->fd,?LOCK_EX);該操作以相互排斥鎖定的方式打開文件。在110行關掉文件close(data->fd);。
看到這裏,我們應該得到的結論是:
在默認情況下,所謂的php?session鎖事實上就是文件鎖
所以,當我們使用session_set_save_handler來自定session操作,改用memcache或其它介質時,僅僅要我們在SessionHandlerInterface的接口中沒有鎖的邏輯。那麽session鎖自然也不會存在。
作者私下也做了這樣的實驗。實踐證明也的確如此。
五、HOW?--?怎樣避免Session鎖帶來的堵塞現象
首先,session鎖不一定是壞事情,在一種情況下就很好用。比如某接口對與同一個用戶的請求默認同一時刻僅僅能運行一次。
這樣的時候。就能夠用seesion_start()和session_write_close()把要堵塞的代碼括起來。很easy暴力有用。
可是大部分時候我們還是要避免這樣的鎖的存在,解決方式:
1、在用完session的時候就立即session_write_close()掉,釋放session鎖
2、採用沒有鎖的session操作,如章節4中所說的用session_set_save_handler來自己定義一個沒有鎖的session操作。
3、再使用默認php?session時,個人比較中意的一個方案:大部分情況下,我們對session的操作基本上都是讀操作。寫操作一般都比較少。
這樣的時候。我們能夠自己寫一個session類。
構造函數:將session讀入cache,關閉session鎖
寫操作:打開session鎖,寫入值,關閉session鎖
讀操作:直接讀cache
部分代碼例如以下:
//將session讀入全局變量$_SEESION
static private function init(){
if(self::$not_init){
session_start();
session_write_close();
self::$not_init = false;
}
}
//讀session
static public function get($name){
self::init();
return $_SESSION[$name];
}
//寫session
static public function set($name, $val){
session_start();
$_SESSION[$name] = $val;
session_write_close();
}
註意:假設是寫操作頻繁的操作,就不適合使用該方法。
?
?
Web提速:避免php session拖慢執行速度