php併發編程-轉自:http://www.cnblogs.com/jingzhishen/p/4328740.htmlphp
併發下常見的加鎖及鎖的PHP具體實現html
http://www.cnblogs.com/scotoma/archive/2010/09/26/1836312.htmlmysql
在最近的項目中有這樣的場景web
1.生成文件的時候,因爲多用戶都有權限進行生成,防止併發下,致使生成的結果出現錯誤,須要對生成的過程進行加鎖,只允許一個用戶在一個時間內進行操做,這個時候就須要用到鎖了,將這個操做過程鎖起來.sql
2.在用了cache的時候,cache失效可能致使瞬間的多數併發請求穿透到數據庫此時也能夠得須要用鎖在同一併發的過程當中將這個操做鎖定.數據庫
針對以上的2種狀況,如今的解決方法是對處理過程進行鎖機制,經過PHP實現以下編程
用到了Eaccelerator的內存鎖 和 文件鎖,原理以下瀏覽器
判斷系統中是否安了EAccelerator 若是有則使用內存鎖,若是不存在,則進行文件鎖緩存
根據帶入的key的不一樣能夠實現多個鎖直接的並行處理,相似Innodb的行級鎖安全
使用以下:
$lock = new CacheLock('key_name');
$lock->lock();
//logic here
$lock->unlock();
//使用過程當中須要注意下文件鎖所在路徑須要有寫權限.
具體類以下:
<?php /** * CacheLock 進程鎖,主要用來進行cache失效時的單進程cache獲取,防止過多的SQL請求穿透到數據庫 * 用於解決PHP在併發時候的鎖控制,經過文件/eaccelerator進行進程間鎖定 * 若是沒有使用eaccelerator則進行進行文件鎖處理,會作對應目錄下產生對應粒度的鎖 * 使用了eaccelerator則在內存中處理,性能相對較高 * 不一樣的鎖之間並行執行,相似mysql innodb的行級鎖 * 本類在sunli的phplock的基礎上作了少量修改 http://code.google.com/p/phplock * @author yangxinqi * */ class CacheLock { //文件鎖存放路徑 private $path = null; //文件句柄 private $fp = null; //鎖粒度,設置越大粒度越小 private $hashNum = 100; //cache key private $name; //是否存在eaccelerator標誌 private $eAccelerator = false; /** * 構造函數 * 傳入鎖的存放路徑,及cache key的名稱,這樣能夠進行併發 * @param string $path 鎖的存放目錄,以"/"結尾 * @param string $name cache key */ public function __construct($name,$path='lock\\') { //判斷是否存在eAccelerator,這裏啓用了eAccelerator以後能夠進行內存鎖提升效率 $this->eAccelerator = function_exists("eaccelerator_lock"); if(!$this->eAccelerator) { $this->path = $path.($this->_mycrc32($name) % $this->hashNum).'.txt'; } $this->name = $name; } /** * crc32 * crc32封裝 * @param int $string * @return int */ private function _mycrc32($string) { $crc = abs (crc32($string)); if ($crc & 0x80000000) { $crc ^= 0xffffffff; $crc += 1; } return $crc; } /** * 加鎖 * Enter description here ... */ public function lock() { //若是沒法開啓ea內存鎖,則開啓文件鎖 if(!$this->eAccelerator) { //配置目錄權限可寫 $this->fp = fopen($this->path, 'w+'); if($this->fp === false) { return false; } return flock($this->fp, LOCK_EX); }else{ return eaccelerator_lock($this->name); } } /** * 解鎖 * Enter description here ... */ public function unlock() { if(!$this->eAccelerator) { if($this->fp !== false) { flock($this->fp, LOCK_UN); clearstatcache(); } //進行關閉 fclose($this->fp); }else{ return eaccelerator_unlock($this->name); } } }
本類在孫立同窗的類的基礎上作了小點改進的了.具體能夠看 http://code.google.com/p/phplock 感謝孫同窗的分享精神!
Apache + PHP 的併發訪問
文章地址:http://www.cnblogs.com/WestContinent/archive/2013/03/25/2981667.html
1.書寫例程:
作成一個測試用的PHP文件代碼以下:
<?php //爲了測試是否多個用戶訪問的時候這個值是公用的,即:是否臨界資源 $count = 0; //循環十次,消耗十秒,模擬一個費時操做 for ($i=1; $i<=10; $i++,$count++) { echo getTime()." i is $i and count is $count.</br>"; sleep(1); } /** * 得到當前時間,返回字符串,包含毫秒數 * 格式:yyyy-MM-dd HH:mm:ss.fff */ function getTime() { $currentTime = Date('Y-m-d H:i:s');//Get currentTime str $milisecond = microtime(); $splitmiliTime = explode('.', $milisecond); $milisecond = $splitmiliTime[1]; $milisecond = substr($milisecond, 0,3); $currentTime = $currentTime.'.'.$milisecond; return $currentTime; } ?>
2.把上面作成的文件Copy到Apache的發佈目錄(通常是Htdocs,可是能夠配置)
3.打開兩個瀏覽器同時訪問發佈PHP頁面
分別獲得以下結果
瀏覽器1
2013-03-25 14:55:42.128 i is 1
and
count
is 0.
2013-03-25 14:55:43.138 i is 2
and
count
is 1.
2013-03-25 14:55:44.152 i is 3
and
count
is 2.
2013-03-25 14:55:45.169 i is 4
and
count
is 3.
2013-03-25 14:55:46.180 i is 5
and
count
is 4.
2013-03-25 14:55:47.194 i is 6
and
count
is 5.
2013-03-25 14:55:48.208 i is 7
and
count
is 6.
2013-03-25 14:55:49.222 i is 8
and
count
is 7.
2013-03-25 14:55:50.236 i is 9
and
count
is 8.
2013-03-25 14:55:51.250 i is 10
and
count
is 9.
|
瀏覽器2
2013-03-25 14:55:41.286 i is 1
and
count
is 0.
2013-03-25 14:55:42.296 i is 2
and
count
is 1.
2013-03-25 14:55:43.310 i is 3
and
count
is 2.
2013-03-25 14:55:44.324 i is 4
and
count
is 3.
2013-03-25 14:55:45.339 i is 5
and
count
is 4.
2013-03-25 14:55:46.355 i is 6
and
count
is 5.
2013-03-25 14:55:47.367 i is 7
and
count
is 6.
2013-03-25 14:55:48.402 i is 8
and
count
is 7.
2013-03-25 14:55:49.645 i is 9
and
count
is 8.
2013-03-25 14:55:50.658 i is 10
and
count
is 9.
|
4.結論
從上面的試驗結果能夠獲得以下結論,由於兩組測試數據中的時間犬牙交錯,兩個用戶在耗時操做中並無出現某一個用戶長時間佔用執行時間片的狀況。說明Apache+Php(loadmodule)是支持多用戶並行操做的。另外全局變量Count在兩個用戶同時訪問的時候都是以全新的狀態出現的,所以Apache+Php(loadmodule)不支持內存緩存數據,也就是說在多用戶併發訪問的狀況下每次訪問都會開闢新的內存。那麼若是須要對多用戶的操做作同步,只能使用文件鎖的方式來實現了。
加鎖解鎖PHP實現
文章地址:http://blog.csdn.net/topasstem8/article/details/6735240
PHP並無完善的線程支持,甚至部署到基於線程模型的httpd服務器都會產生一些問題,但即便是多進程模型下的PHP,也不免出現多進程共同訪問同一 資源的狀況。好比整個程序共享的數據緩存,或者由於資源受限而必須對特定處理過程進行排隊,以及針對每一個用戶生成惟一的某種標識的情形。PHP語言自身沒 有提供進程互斥和鎖定機制,於是使得在這些狀況下的編程遇到了困難,目前瞭解到的可選的辦法有如下這些:
一、利用MySQL的鎖定機制來實現互斥。缺點是增大了數據庫服務器的鏈接負擔,而且使得程序依賴於數據庫服務才能正常工做。
二、利用文件鎖機制。也就是利用flock函數經過文件實現鎖定和互斥機制,來模擬通用編程模型下的鎖定原語的工做方式。這種方式在之前以純文本文件爲存儲引擎的時代成爲保護數據完整性的必備元素,如今在使用文本文件做爲緩存媒介的場合也至關常見。PmWiki應該也是使用了這個機制來對多人同時編輯一個頁面的情形進行提醒。不過文件鎖機制多少會調用到宿主操做系統上的文件鎖特性,所以在使用時必定要檢查服務器操做系統是否爲PHP環境提供了完善可靠的文件鎖機制。
三、利用共享內存空間計數。PHP能夠利用shmop_open函數開闢一塊內存空間,在服務進程之間共享數據,爲了保證共享數據的互斥安全訪問,可使用 sem_get、sem_acquire和sem_release這組函數實現共享計數鎖定機制。這種辦法在後臺實際是調用了系統的ipc 服務來實現。
用 PHP 編寫支持高併發的網站,須要作什麼處理?
文章地址:http://www.zhihu.com/question/20049768
一、Webserver (Nginx) :這一層是能夠輕鬆分佈式部署的,結合智能DNS解析能夠簡易地防止單點故障、實現區域訪問加速,結合LVS很容易實現負載均衡。這一層主要是負責處理靜態請求和轉發PHP請求至第二層的PHP處理節點,至於靜態資源地址(http://misc.xxxx.com)能夠單獨拿出來部署,或者直接使用商用的雲存儲服務(國內七牛不錯,國外有Amazon S3)
二、PHP 處理節點:一個節點其實就是一個監聽特定端口的系統進程,webserver的請求經過負載均衡器(我用的AWS的loadbalancer)進行分發, 很好實現分佈式和負載均衡。我如今用的仍是php自帶的php-fpm,其實facebook出的hhvm性能很是強悍,可是還不能100%經過我項目的 單元測試,等hhvm成熟事後能夠平滑替換
三、高速緩存:用的memcached,這一層的做用主要是減輕數據庫IO和加快熱數據 訪問,緩存策略與程序耦合度較高,不贅述,但簡單地說有兩種方式,一種是在程序的全局層面加一個緩存處理,這種方法代碼耦合度低,可是有效命中率不高,有 些項目不必定適應,另外一種是在具體的數據存取處加緩存處理,這種辦法程序耦合度較高,可是緩存命中率很是高,幾乎沒有無效緩存存在,我用的是這種。
四、數據庫 :我如今的項目數據規模不大,暫時只用了單臺數據庫,可是程序邏輯上已作好了數據庫線性擴展的準備。其實數據庫層的擴展是老生常談了,經常使用手段是分庫分 表,這一塊須要在前期的代碼就打下基礎,另外更平滑地手段是使用中間件,好比360的Atlas,阿里巴巴的cobar,淘寶的TDDL,中間件能夠在不 大範圍變動代碼的狀況下擴展,可是具體的使用場景仍是有限的,具體項目還需單獨考察。
五、其 他:根據不一樣的項目,架構還能夠選擇性地使用隊列,我如今用的beantalkd,Redis也是一個很好的選擇。隊列經常使用的使用環境是郵件發送和站內消息推送上面,可是在某些場景下也能夠做爲核心數據庫的緩衝,對應對大併發或者突發性流量也是不錯的選擇