PHP讀寫檔高併發處理操作步驟分析
這次帶給大家PHP讀寫檔高並發處理操作步驟分析,PHP讀寫檔高並發處理的注意事項有哪些,下面就是實戰案例,一起來看一下。
最近公司遊戲開發需要知道遊戲載入的流失率。因為,我們做的是網頁遊戲。玩過網頁遊戲的人都知道,進入遊戲前要先載入一些資源。最後才能到達創造角色的遊戲介面。我們有一個需求就是要統計在載入過程中還未到達角色建立介面而流失的使用者數量。
我們在載入開始就進行統計人數,載入完成之後再記錄人數。這樣,透過用加載前的人數減去成功加載後的人數。就知道了加載的流失率。就可以知道遊戲是否還要繼續優化載入過程,降低用戶載入遊戲率。
由於,我們的量都是從*主流的合作媒體進行導量過來。所以,並發非常高,據粗略計算應該可以達到每秒1000左右的並發數量。
載入前的人數本來想放到遊戲內部的快取平台。但是,遊戲後端的同事擔心並發太高,導致資源無故浪費。因為,記憶體的釋放並不是即時回應的。所以,將統計的人數放到在另外一台伺服器:統計伺服器。
我剛開始採用的方案如下:
透過php的<a href="http://www.php.cn/wiki/1311.html" target="_blank">file_get_contents</a>()
與<a href="http://www.php.cn/wiki/1312.html" target="_blank">file_put_contents</a>( )
進行讀取與寫入。第一次讀寫就向文件寫入1,第二次加載就在原來的基礎上加1.以此類推.這種順序的思想完全不存在任何問題。問題就出在,我們的伺服器不可能是順序形式的。
準確的說,並發的存取不是順序的。當A玩家載入遊戲讀取到檔案裡面的數字100(假如這時是100),B玩家讀取到的也是100,這時,處理A玩家的線程就是在100的基礎上加1,得到101,就會向文件寫入101。
處理B玩家的執行緒也得到相同的結果,將101寫入檔案。這時,問題就出現了? B玩家是在A玩家之後載入遊戲的,理應得到102的計算結果。
這是同時發生的問題。這時候,我想到了採用fopen()
打開文件,並用flock()
加一個寫入鎖定。大家一定會認為,這種方式有了鎖定,那就不會造成問題了。其實,也是錯的。
因為,我們的問題不是出在寫入上面。而是讀取的時候造成資料的不同步。 OK。到這裡,我實在百度谷歌都搞不定了。
當希望寄託在PHP函數本身而夢碎的時候,我只能另尋它法。脫離它。於是,我想到了*語言的Map映射的機制。類似於我們的PHP數組,每加載一次就我往數組中添加一個元素。這樣,到最後我只需要count()
一下陣列就知道了有多少玩家載入了遊戲。
但是,用陣列的話,也存在一個問題。就是PHP的變數還是常數,在腳本執行完畢之後都會自己清除。於是,我想到了文件保存的方式。
最終的可行方案思路如下:
用fopen開啟一個文件,以只寫的方式。然後寫鎖定。玩家每載入一次我就寫入檔案裡面一個數字1,最後得到的檔案內容透過file_get_contents()
一次讀取出來,再用strlen()
計算長度即知道了有多少玩家載入了遊戲。
聽聞flock()
函數會鎖定會造成系統資源在很多時間升高。所以,我採用大家所使用的方式,用微秒超時的技術解決這個問題。如果,走出這個時間我就*掉它。具體的程式碼如下:
// loadcount.func.php 函数文件。 /** * 获取某来源和某服务器ID的游戏加载次数。 * * @param string $fromid 来源标识。 * @param int $serverid 服务器ID编号。 * * @return int */ function getLoadCount($fromid, $serverid) { global $g_global; $serverid = (int) $serverid; $fromid = md5($fromid); $filename = $fromid . $serverid . '.txt'; $data = file_get_contents($filename); return strlen($data); } /** * 获取某来源所有服务器的游戏加载次数。 * * @param string $fromid 来源标识。 * * @return int */ function getAllLoadCount($fromid) { global $g_global; $fromid = md5($fromid); $count = 0; foreach (glob("{$fromid}*.txt") as $filename) { $file_content = file_get_contents($filename); $count += strlen($file_content); } return $count; } /** * 清空所有的加载数据。 * * @return void */ function clearLoadCount() { foreach (glob("*.txt") as $filename) { unlink($filename); } return true; } /** * 延迟更新游戏加载次数中间件。 * * 使用此函数来延迟更新数据,原理:当不足1000次的时候,不更新数据库,超过1000就更新到数据库里面去。 * * @param string $fromid 来源标识。 * @param int $serverid 服务器ID编号。 */ function delayAddLoadCount($fromid, $serverid) { // 使用MD5生成文件名记录缓存次数。 $fromid = md5($fromid); $filename = $fromid . $serverid . '.txt'; if($fp = fopen($filename, 'a')) { $startTime = microtime(); do { $canWrite = flock($fp, LOCK_EX); if(!$canWrite) { usleep(round(mt_rand(0, 100)*1000)); } } while ( ( !$canWrite ) && ( ( microtime()- $startTime ) < 1000 ) ); if ($canWrite) { fwrite($fp, "1"); } fclose($fp); } return true; }
以下是我呼叫以上方法的檔案:
< ?php /** * @describe 平台用户加载游戏次数统计接口入口。 * @date 2012.12.17 */ include_once './loadcount.func.php'; // 测试用。 // $_GET['fromid'] = '4399'; // $_GET['serverid'] = mt_rand(0, 5); // 添加加载次数。 if ( $_GET['action'] == 'addcount' ) { $fromid = $_GET['fromid']; // 来源标识。 $serverid = $_GET['serverid']; // 服务器ID编号。 $return = delayAddLoadCount($fromid, $serverid); $return = $return ? 1 : 0; ob_clean(); echo json_encode($return); exit; } // 取加载次数。 elseif ( $_GET['action'] == 'getcount' ) { $fromid = $_GET['fromid']; // 来源标识。 if ( !isset( $_GET['serverid'] ) ) // 有服务器编号 ID则取来源对应的服务器加载次数。 { $count = getAllLoadCount($fromid); } else // 加载对应来源的次数。 { $serverid = $_GET['serverid']; // 服务器ID编号。 $count = getLoadCount($fromid, $serverid); } ob_clean(); header('Content-Type:text/html;charset=UTF-8'); $serverid = strlen($serverid) ? $serverid : '无'; echo "来源:{$fromid},服务器ID:{$serverid},游戏加载次数:" . $count; exit; } // 清除加载次数。 elseif ( $_GET['action'] == 'clearcount' ) { header('Content-Type:text/html;charset=UTF-8'); $return = clearLoadCount(); if ($return) { echo "清除成功!"; } else { echo "清除失败!"; } }
這是血的教訓,所以,我不得不將它記錄下來。以備以後讓他人借鏡。
本文是作者寒冰一年前在4399遊戲工作室負責做數據分析的時候寫的程式碼。希望對大家有幫助。
PHP数据库操作之高并发实例
高并发下PHP写文件日志丢失
<?php /** * Created by PhpStorm. * User: andyfeng * Date: 2015/6/24 * Time: 13:31 */ class LogFileUtil { public static $fileHandlerCache; private static $initFlag = false; private static $MAX_LOOP_COUNT = 3; private static function init() { self::$initFlag = true; register_shutdown_function(array("LogFileUtil", "shutdown_func")); } /** * 输出到文件日志 * @param $filePath 文件路径 * @param $msg 日志信息 * @return int */ public static function out($filePath, $msg) { if (!self::$initFlag) { self::init(); } return self::internalOut($filePath, $msg); } /** * @param $filePath * @param $msg * @param $loop * @return int */ private static function internalOut($filePath, $msg, $loop = 0) { //以防一直添加失败造成死循环 if ($loop > self::$MAX_LOOP_COUNT) { $result = 0; } else { $loop++; $fp = self::$fileHandlerCache["$filePath"]; if (empty($fp)) { $fp = fopen($filePath, "a+"); self::$fileHandlerCache[$filePath] = $fp; } if (flock($fp, LOCK_EX)) { $result = fwrite($fp, $msg); flock($fp, LOCK_UN); } else { $result = self::internalOut($filePath, $msg, $loop); } } return $result; } function shutdown_func() { if (!empty(LogFileUtil::$fileHandlerCache)) { if (is_array(LogFileUtil::$fileHandlerCache)) { foreach (LogFileUtil::$fileHandlerCache as $k => $v) { if (is_resource($v)) //file_put_contents("close.txt",$k); fclose($v); } } } } }
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上是PHP讀寫檔高併發處理操作步驟分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP 8.4 帶來了多項新功能、安全性改進和效能改進,同時棄用和刪除了大量功能。 本指南介紹如何在 Ubuntu、Debian 或其衍生版本上安裝 PHP 8.4 或升級到 PHP 8.4

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

本教程演示瞭如何使用PHP有效地處理XML文檔。 XML(可擴展的標記語言)是一種用於人類可讀性和機器解析的多功能文本標記語言。它通常用於數據存儲

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

字符串是由字符組成的序列,包括字母、數字和符號。本教程將學習如何使用不同的方法在PHP中計算給定字符串中元音的數量。英語中的元音是a、e、i、o、u,它們可以是大寫或小寫。 什麼是元音? 元音是代表特定語音的字母字符。英語中共有五個元音,包括大寫和小寫: a, e, i, o, u 示例 1 輸入:字符串 = "Tutorialspoint" 輸出:6 解釋 字符串 "Tutorialspoint" 中的元音是 u、o、i、a、o、i。總共有 6 個元

PHP和Python各有優勢,選擇依據項目需求。 1.PHP適合web開發,尤其快速開發和維護網站。 2.Python適用於數據科學、機器學習和人工智能,語法簡潔,適合初學者。

PHP的魔法方法有哪些? PHP的魔法方法包括:1.\_\_construct,用於初始化對象;2.\_\_destruct,用於清理資源;3.\_\_call,處理不存在的方法調用;4.\_\_get,實現動態屬性訪問;5.\_\_set,實現動態屬性設置。這些方法在特定情況下自動調用,提升代碼的靈活性和效率。

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7
