本文实例讲述了PHP读写文件高并发处理操作。分享给大家供大家参考,具体如下:
背景:
最近公司游戏开发需要知道游戏加载的流失率。因为,我们做的是网页游戏。玩过网页游戏的人都知道,进入游戏前要加载一些资源。最后才能到达创建角色的游戏界面。我们有一个需求就是要统计在加载过程中还未到达角色创建界面而流失的用户数量。
我们在加载开始就进行统计人数,加载完成之后再记录人数。这样,通过用加载前的人数减去成功加载后的人数。就知道了加载的流失率。就可以知道游戏是否还要继续优化加载过程,降低用户加载游戏率。
由于,我们的量都是从*主流的合作媒体进行导量过来。所以,并发非常高,据粗略计算应该能达到每秒1000左右的并发数量。
加载前的人数本来想放到游戏内部的缓存平台。但是,游戏后端的同事担心并发太高,导致资源无故浪费。因为,内存的释放并不是实时响应的。所以,将统计的人数放到在另外一台服务器:统计服务器。
我刚开始采用的方案如下:
通过php的file_get_contents()
与file_put_contents()
进行读取与写入。第一次读写就向文件写入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; }
以下是我调用以上方法的文件:
< "来源:{$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写文件日志丢失
<"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缓存技术总结》、《PHP运算与运算符用法总结》、《PHP基本语法入门教程》、《php面向对象程序设计入门教程》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》
希望本文所述对大家PHP程序设计有所帮助。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
P70系列延期,华为新旗舰将在下月发布
3月20日消息,近期博主@数码闲聊站 透露,原定三月份发布的华为新旗舰P70系列延期发布,预计4月份上市。
而博主@定焦数码 爆料,华为的P70系列在定位上已经超过了Mate60,成为了重要的旗舰系列之一。它肩负着重返影像领域顶尖的使命。那么这次P70会带来哪些令人惊艳的创新呢?
根据目前爆料的消息来看,华为P70系列将推出三个版本,其中P70和P70 Pro采用了三角形的摄像头模组设计,而P70 Art则采用了与上一代P60 Art相似的不规则形状设计。这样的外观是否好看见仁见智,但辨识度绝对拉满。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]