PHP并发下安全读写文件函数

mengkun 23.5K 44

众所周知,在高并发的状态下,直接使用 PHP 读写同一个文件时,可能会导致文件内容丢失,于是乎就需要额外的代码来解决这个问题。大致的思路是先使用 flock 函数对原文件进行锁死,再来读写。

下面的这个函数是从大名鼎鼎的可道云的代码中找到的。并不简单可道云相信大家都不会陌生,它是一个无数据库的程序,因此配置存储全都是靠这个函数完成的,所以这段代码的安全性和普适性绝对毋庸置疑,可以放心的用于项目中(注意尽量保留原作者的版权信息就行了)。

代码的原版位于可道云的 /app/function/file.function.php 第 729 行左右。原版代码的逻辑是 写文件时如果原文件不存在,则直接返回 false,我把这一部分稍微修改了一下,改成了 如果目标文件不存在,则创建文件并写入。鼓掌

不多说了,全部的代码如下:

PHP
  1. <?php
  2.  
  3. /**
  4. * @link http://kodcloud.com/
  5. * @author warlee | e-mail:kodcloud@qq.com
  6. * @copyright warlee 2014.(Shanghai)Co.,Ltd
  7. * @license http://kodcloud.com/tools/license/license.txt
  8. */
  9.  
  10.  
  11. /**
  12. * 安全读取文件,避免并发下读取数据为空
  13. *
  14. * @param $file 要读取的文件路径
  15. * @param $timeout 读取超时时间
  16. * @return 读取到的文件内容 | false - 读取失败
  17. */
  18. function file_read_safe($file, $timeout = 5) {
  19. if (!$file || !file_exists($file)) return false;
  20. $fp = @fopen($file, 'r');
  21. if (!$fp) return false;
  22. $startTime = microtime(true);
  23. // 在指定时间内完成对文件的独占锁定
  24. do {
  25. $locked = flock($fp, LOCK_EX | LOCK_NB);
  26. if (!$locked) {
  27. usleep(mt_rand(1, 50) * 1000); // 随机等待1~50ms再试
  28. }
  29. }
  30. while ((!$locked) && ((microtime(true) - $startTime) < $timeout));
  31. if ($locked && filesize($file) >= 0) {
  32. $result = @fread($fp, filesize($file));
  33. flock($fp, LOCK_UN);
  34. fclose($fp);
  35. if (filesize($file) == 0) {
  36. return '';
  37. }
  38. return $result;
  39. } else {
  40. flock($fp, LOCK_UN);
  41. fclose($fp);
  42. return false;
  43. }
  44. }
  45.  
  46. /**
  47. * 安全写文件,避免并发下写入数据为空
  48. *
  49. * @param $file 要写入的文件路径
  50. * @param $buffer 要写入的文件二进制流(文件内容)
  51. * @param $timeout 写入超时时间
  52. * @return 写入的字符数 | false - 写入失败
  53. */
  54. function file_write_safe($file, $buffer, $timeout = 5) {
  55. clearstatcache();
  56. if (strlen($file) == 0 || !$file) return false;
  57. // 文件不存在则创建
  58. if (!file_exists($file)) {
  59. @file_put_contents($file, '');
  60. }
  61. if(!is_writeable($file)) return false; // 不可写
  62. // 在指定时间内完成对文件的独占锁定
  63. $fp = fopen($file, 'r+');
  64. $startTime = microtime(true);
  65. do {
  66. $locked = flock($fp, LOCK_EX);
  67. if (!$locked) {
  68. usleep(mt_rand(1, 50) * 1000); // 随机等待1~50ms再试
  69. }
  70. }
  71. while ((!$locked) && ((microtime(true) - $startTime) < $timeout));
  72. if ($locked) {
  73. $tempFile = $file.'.temp';
  74. $result = file_put_contents($tempFile, $buffer, LOCK_EX);
  75. if (!$result || !file_exists($tempFile)) {
  76. flock($fp, LOCK_UN);
  77. fclose($fp);
  78. return false;
  79. }
  80. @unlink($tempFile);
  81. ftruncate($fp, 0);
  82. rewind($fp);
  83. $result = fwrite($fp, $buffer);
  84. flock($fp, LOCK_UN);
  85. fclose($fp);
  86. clearstatcache();
  87. return $result;
  88. } else {
  89. flock($fp, LOCK_UN);
  90. fclose($fp);
  91. return false;
  92. }
  93. }
复制 文本 高亮

后记

可道云的代码里其实还有大量的封装好的非常好用的函数,仔细研究研究绝对受益匪浅。

发表评论 取消回复
表情 图片 链接 代码

  1.  
      Lv 2

    大佬我来问一下,请问你之前写的无数据库配置读写类,咋和这个结合啊

  2. dd
    dd Lv 1

    文件锁

  3. lzw
    lzw Lv 1

    这个write写成wirte也是很迷

    • mengkun
      mengkun 站长

      @lzw哈哈哈哈,确实很迷……已更正!

  4. 韦贝贝
    韦贝贝 Lv 1

    孟坤大佬php数据库高并发怎么写呀,希望能出一期php数据库高并发的。

    • mengkun
      mengkun 站长

      @韦贝贝数据库本身就是支持高并发的

  5. 月色.
    月色. Lv 1

    可以博主联系我下吗?

  6. 月色.
    月色. Lv 1

    想买,好像特麻烦。

  7. 杰新博客
    杰新博客 Lv 3

    这么久没更,大家都等的有些急躁了。O(∩_∩)O哈哈~

  8. 266277
    266277 Lv 4

    开工啦催更啦

  9. 完美者
    完美者 Lv 5

    过节去了?

  10. 完美者
    完美者 Lv 5

    年过完了,是不是该更了

  11. 子成
    子成 Lv 1

    孟坤大佬的表情包好爽啊

  12. 萧长风
    萧长风 Lv 2

    都一个月多了。。。还不更新?
    至少,先来一篇满足一下啊!

  13. doctorrm
    doctorrm Lv 2

    你大爷的(求),还不更新(更)

  14. 男人不可以穷
    男人不可以穷 Lv 2

    一不小心就模仿了你的主题dog6,确实挺不错的

  15. 241867646@qq.com
    241867646@qq.com Lv 1

    刚才发现一点你的友链就出现错误.改了半天,现在终于好了

  16. 241867646@qq.com
    241867646@qq.com Lv 1

    大大写了一个导航网站.现在准备坚持运营.能交换友链嘛.我把你的添加到我导航网底部了.你有空看下 嘟嘟动漫导航

    • mengkun
      mengkun 站长

      @241867646@qq.com非常感谢!不过我站现在已经不交换友链了

      • 241867646@qq.com
        241867646@qq.com Lv 1

        @mengkun没事.你的博客我会一直挂到我不做的时候.我想那估计在几年后了.嘻嘻 嘟嘟动漫导航

  17. 2802580445
    2802580445 Lv 1

    孟坤大大,我很喜欢你实验室里的文字游戏,玩了很久,想要一份文字游戏的源码,能不能发给我。邮箱2802580445@qq.com

    • mengkun
      mengkun 站长

      @2802580445这个代码你只要稍微花点心思就能找到

    • 266277
      266277 Lv 4

      @2802580445右键是个好东西

  18. 醉后
    醉后 Lv 1

    孟坤大大,我很喜欢你实验室里的文字游戏,我想要一份那个文字游戏的源码可以吗。邮箱2802580445@qq.com

  19. Aliwen创想博客
    Aliwen创想博客 Lv 1

    大佬,,交换下友链吧!感觉你这个主题不错呢
    欢迎访问:http://aliwen.baomanxi.top

  20. 李逍遥
    李逍遥 Lv 1

    查看图片 好看不

  21. 完美者
    完美者 Lv 5

    电脑修好了,来水一波,纪念下

  22. 吴凯旋
    吴凯旋 Lv 1

    坤哥,我想买主题,但是我还想问一些问题,找遍了你的网站,也没找到QQ号,咋办?

  23. 逍遥
    逍遥 Lv 1

    大大大佬...

  24. 男人不可以穷
    男人不可以穷 Lv 2

    查水表

  25. 阿珏
    阿珏 Lv 3

    吐吐吐......槽

  26. 李逍遥
    李逍遥 Lv 1

    来访大佬

  27. 完美者
    完美者 Lv 5

    请把全文翻译成中文

    • mengkun
      mengkun 站长

      @完美者我点开文章一看,这文章没有年代,歪歪斜斜的每页上都写着‘代码’两个字。我横竖睡不着,仔细看了半夜,才从字缝里看出字来,满本都写着七个字是‘看不懂就多读书’! 斜眼笑

  28. 杨小杰博客
    杨小杰博客 Lv 3

    前排留名

  29. 惶心
    惶心 Lv 2

    前排围观孟坤大佬秀技术!
    顺带大佬的博客用海外线路是真心打不开 切了三次代理才打开了
    建议海外通过 Geo DNS 解析一层 Cloudflare(通过 CNAME 接入)
    如果不知道怎么做的话最近博客大概会有教程(((

    • mengkun
      mengkun 站长

      @惶心懒得弄了。国外无法访问更好,惹不起外国的奇葩法律(此处特指欧盟的隐私条款)并不简单

      ……

      说得好像我的博客有外国访客一样允悲

    • 266277
      266277 Lv 4

      @惶心说的你好像住外国一样

    • 完美者
      完美者 Lv 5

      @惶心说的好像有人访问一样

  30. 266277
    266277 Lv 4

    好水啊!!!