当前位置:首页 > PHP教程 > php文件操作 > 列表

php文件锁产生的问题和解决方案(一个真实案例)

发布:smiling 来源: PHP粉丝网  添加日期:2022-06-11 08:59:33 浏览: 评论:0 

一个真实案例:想起自己之前犯过一个相关的错误。当时场景是这样的:有一个微信公众号项目,调用微信公众号的接口都需要access_token,它的有效期是2小时。当时我的做法是把它存放在文件中,格式使用的是json。

{"access_token":"easWasdw32323", "expire":1588219064}。伪代码如下:

  1. function getToken ($tokenFile) 
  2.  
  3.  
  4.     $tokenJson = file_get_contents($tokenFile); 
  5.  
  6.       
  7.  
  8.     if (!$tokenJson) { 
  9.  
  10.         $token = loadToken($tokenFile); 
  11.  
  12.     } else if (json_decode($tokenJson, true)['expire'] <= time()){ 
  13.  
  14.         $token = loadToken($tokenFile); 
  15.  
  16.     } else { 
  17.  
  18.         $token = json_decode($tokenJson, true)['access_token']; 
  19.  
  20.     } 
  21.  
  22.       
  23.  
  24.     return $token; 
  25.  
  26.  
  27. function loadToken ($tokenFile)  
  28.  
  29.  
  30.     $fp = fopen($tokenFile, 'r+'); 
  31.  
  32.       
  33.  
  34.     $tokenJson = ...; // 调用微信接口获取到token 
  35.  
  36.     fwrite($fp, json_encode($tokenJson)); 
  37.  
  38.       
  39.  
  40.     return $tokenJson['access_token']; 
  41.  

出现的问题:

项目运行一段时间后就会出问题,但过一两秒后再刷新就正常了。

问题原因分析:

假设token已经过期了,这时候有2个请求来了,分别命名为A、B。A来了,发现token到期后,去调用微信接口获取新的token,获取后,更新到存放token的文件中。

但是,文件没有完全更新完毕的时候,B来了,读入存放token的文件。因为token文件中数据没有更新完整,B读到的数据就会产生错误。

另外还有可能是A和B同时在更新文件内容,这样就会产生数据混乱,也会导致错误发生。

如何规避这个错误呢?

文件锁机制可以完成。

在PHP中提供了 flock()函数,可以对文件使用锁定机制(锁定或释放文件)。当一个进程在访问文件时加上锁,其他进程要想对该文件进行访问,则必须等到锁定被释放以后。这样就可以避免在并发访问同一个文件时破坏数据。

函数原型如下:

flock ( resource $handle , int $operation [, int &$wouldblock ] ) : bool

handle:

文件系统指针,是典型地由 fopen() 创建的 resource(资源)。

operation

operation 可以是以下值之一:

LOCK_SH取得共享锁定(读取的程序)。

LOCK_EX 取得独占锁定(写入的程序)。

LOCK_UN 释放锁定(无论共享或独占)。

LOCK_NB附加锁定(Windows 上还不支持)。

wouldblock

如果锁定会堵塞的话(EWOULDBLOCK 错误码情况下),可选的第三个参数会被设置为 TRUE。(Windows 上不支持)

demo

demo1.php

  1. <?php 
  2.  
  3. $file = 'data.txt'
  4.  
  5. $handler = fopen($file'a+'or die('文件资源打开失败'); 
  6.  
  7. // 取得独占锁 
  8.  
  9. if (flock($handler, LOCK_EX)) { 
  10.  
  11.     sleep(5); 
  12.  
  13.     flock($handler, LOCK_UN); 
  14.  
  15. else { 
  16.  
  17.     echo '锁定失败'
  18.  
  19.  
  20. fclose($handler); 

demo2.php

  1. <?php 
  2.  
  3. $file = 'data.txt'
  4.  
  5. $handler = fopen($file'a+'or die('文件资源打开失败'); 
  6.  
  7.    
  8.  
  9. // 取得独占锁 
  10.  
  11. if (flock($handler, LOCK_EX)) { 
  12.  
  13.     fwrite($handler'sometest string'); 
  14.  
  15.     flock($handler, LOCK_UN); 
  16.  
  17. else { 
  18.  
  19.     echo '锁定失败'
  20.  
  21.  
  22. fclose($handler); 

先运行demo1.php然后立即运行demo2.php,会发现,因为被demo1.php锁定了文件,demo2.php写入不了新内容,只有等demo1.php释放了锁定,demo2.php才能拿到独占锁,然后才能写入文件。

解决问题

学完这些知识后,就能解决我之前的问题了,改进的代码如下:

  1. <?php 
  2.  
  3. function getToken ($tokenFile){ 
  4.  
  5.     $tokenJson = file_get_contents($tokenFile); 
  6.  
  7.       
  8.  
  9.     if (!$tokenJson) { 
  10.  
  11.             $token = loadToken($tokenFile);     
  12.  
  13.     } else if (json_decode($tokenJson, true)['expire'] <= time()){  
  14.  
  15.            $token = loadToken($tokenFile); 
  16.  
  17.     } else { 
  18.  
  19.             $token = json_decode($tokenJson, true)['access_token'];     
  20.  
  21.     } 
  22.  
  23.     return $token
  24.  
  25.  
  26.  
  27.  
  28. function loadToken ($tokenFile) { 
  29.  
  30.     $fp = fopen($tokenFile'w');    // 取得独占锁     
  31.  
  32.     if (flock($fp, LOCK_EX)) { 
  33.  
  34.         $tokenJson = ...; // 调用微信接口获取到token      
  35.  
  36.         fwrite($fp, json_encode($tokenJson));  
  37.  
  38.         flock($fp, LOCK_UN);     
  39.  
  40.     } else { 
  41.  
  42.         return false;     
  43.  
  44.     } 
  45.  
  46.       
  47.  
  48.     return $tokenJson['access_token']; 
  49.  
  50. }

Tags: php文件锁

分享到: