1. 程式人生 > >Web提速:避免php session拖慢執行速度

Web提速:避免php session拖慢執行速度

返回 eve ack type 是我 介質 alt ajax請求 -m

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拖慢執行速度