众所周知,在高并发的状态下,直接使用 PHP 读写同一个文件时,可能会导致文件内容丢失,于是乎就需要额外的代码来解决这个问题。大致的思路是先使用 flock
函数对原文件进行锁死,再来读写。
下面的这个函数是从大名鼎鼎的可道云的代码中找到的。[wb_并不简单]可道云相信大家都不会陌生,它是一个无数据库的程序,因此配置存储全都是靠这个函数完成的,所以这段代码的安全性和普适性绝对毋庸置疑,可以放心的用于项目中(注意尽量保留原作者的版权信息就行了)。
代码的原版位于可道云的 /app/function/file.function.php
第 729 行左右。原版代码的逻辑是 写文件时如果原文件不存在,则直接返回 false
,我把这一部分稍微修改了一下,改成了 如果目标文件不存在,则创建文件并写入。[wb_鼓掌]
不多说了,全部的代码如下:
<?php /** * @link http://kodcloud.com/ * @author warlee | e-mail:[email protected] * @copyright warlee 2014.(Shanghai)Co.,Ltd * @license http://kodcloud.com/tools/license/license.txt */ /** * 安全读取文件,避免并发下读取数据为空 * * @param $file 要读取的文件路径 * @param $timeout 读取超时时间 * @return 读取到的文件内容 | false - 读取失败 */ function file_read_safe($file, $timeout = 5) { if (!$file || !file_exists($file)) return false; $fp = @fopen($file, 'r'); if (!$fp) return false; $startTime = microtime(true); // 在指定时间内完成对文件的独占锁定 do { $locked = flock($fp, LOCK_EX | LOCK_NB); if (!$locked) { usleep(mt_rand(1, 50) * 1000); // 随机等待1~50ms再试 } } while ((!$locked) && ((microtime(true) - $startTime) < $timeout)); if ($locked && filesize($file) >= 0) { $result = @fread($fp, filesize($file)); flock($fp, LOCK_UN); fclose($fp); if (filesize($file) == 0) { return ''; } return $result; } else { flock($fp, LOCK_UN); fclose($fp); return false; } } /** * 安全写文件,避免并发下写入数据为空 * * @param $file 要写入的文件路径 * @param $buffer 要写入的文件二进制流(文件内容) * @param $timeout 写入超时时间 * @return 写入的字符数 | false - 写入失败 */ function file_write_safe($file, $buffer, $timeout = 5) { clearstatcache(); if (strlen($file) == 0 || !$file) return false; // 文件不存在则创建 if (!file_exists($file)) { @file_put_contents($file, ''); } if(!is_writeable($file)) return false; // 不可写 // 在指定时间内完成对文件的独占锁定 $fp = fopen($file, 'r+'); $startTime = microtime(true); do { $locked = flock($fp, LOCK_EX); if (!$locked) { usleep(mt_rand(1, 50) * 1000); // 随机等待1~50ms再试 } } while ((!$locked) && ((microtime(true) - $startTime) < $timeout)); if ($locked) { $tempFile = $file.'.temp'; $result = file_put_contents($tempFile, $buffer, LOCK_EX); if (!$result || !file_exists($tempFile)) { flock($fp, LOCK_UN); fclose($fp); return false; } @unlink($tempFile); ftruncate($fp, 0); rewind($fp); $result = fwrite($fp, $buffer); flock($fp, LOCK_UN); fclose($fp); clearstatcache(); return $result; } else { flock($fp, LOCK_UN); fclose($fp); return false; } }
后记
可道云的代码里其实还有大量的封装好的非常好用的函数,仔细研究研究绝对受益匪浅。
本文作者为mengkun,转载请注明。
大佬我来问一下,请问你之前写的无数据库配置读写类,咋和这个结合啊
文件锁
这个write写成wirte也是很迷[aru_25]
@lzw哈哈哈哈,确实很迷……已更正![aru_15]
孟坤大佬php数据库高并发怎么写呀,希望能出一期php数据库高并发的。
@韦贝贝数据库本身就是支持高并发的
[aru_159]可以博主联系我下吗?
想买,好像特麻烦。
这么久没更,大家都等的有些急躁了。O(∩_∩)O哈哈~[aru_16]
开工啦催更啦
过节去了?
年过完了,是不是该更了
孟坤大佬[aru_59]的表情包好爽啊
[aru_13]都一个月多了。。。还不更新?
至少,先来一篇满足一下啊!
你大爷的(求),还不更新(更)[aru_142]
一不小心就模仿了你的主题[wb_dog6],确实挺不错的
刚才发现一点你的友链就出现错误.改了半天,现在终于好了[aru_19]
大大写了一个导航网站.现在准备坚持运营.能交换友链嘛.我把你的添加到我导航网底部了.你有空看下 嘟嘟动漫导航
@[email protected]非常感谢!不过我站现在已经不交换友链了[aru_2]
@mengkun没事.你的博客我会一直挂到我不做的时候.我想那估计在几年后了.嘻嘻 嘟嘟动漫导航
孟坤大大,我很喜欢你实验室里的文字游戏,玩了很久,想要一份文字游戏的源码,能不能发给我。[aru_1]邮箱[email protected]
@2802580445这个代码你只要稍微花点心思就能找到
@2802580445右键是个好东西
[aru_1]孟坤大大,我很喜欢你实验室里的文字游戏,我想要一份那个文字游戏的源码可以吗。邮箱[email protected]
大佬,,交换下友链吧!感觉你这个主题不错呢
欢迎访问:http://aliwen.baomanxi.top
@Aliwen创想博客我们交换吧
查看图片 好看不
电脑修好了,来水一波,纪念下
坤哥,我想买主题,但是我还想问一些问题,找遍了你的网站,也没找到QQ号,咋办?
大大大佬...[aru_3]
查水表[aru_1][aru_2]
吐吐吐......槽[aru_59]
来访大佬
请把全文翻译成中文
@完美者我点开文章一看,这文章没有年代,歪歪斜斜的每页上都写着‘代码’两个字。我横竖睡不着,仔细看了半夜,才从字缝里看出字来,满本都写着七个字是‘看不懂就多读书’! [斜眼笑]
前排留名
前排围观孟坤大佬秀技术!
顺带大佬的博客用海外线路是真心打不开 切了三次代理才打开了
建议海外通过 Geo DNS 解析一层 Cloudflare(通过 CNAME 接入)
如果不知道怎么做的话最近博客大概会有教程(((
@惶心懒得弄了。国外无法访问更好,惹不起外国的奇葩法律(此处特指欧盟的隐私条款)[wb_并不简单]
……
说得好像我的博客有外国访客一样[wb_允悲]
@惶心说的你好像住外国一样
@惶心说的好像有人访问一样
好水啊!!!