更新json文件_【真实案例】php文件锁产生的问题和解决方案

2ca675ea5312096c03cf8c01426e512a.png

php中文网最新课程

每日17点准时技术干货分享

58fbe2ac047837aa4fc088c4a32533eb.png

9b3f1c0197710c4eb7593b36df59d4fa.gif

本文为php中文网认证作者:“齐天大圣”投稿,欢迎加入php中文网有偿投稿计划!

一个真实案例

想起自己之前犯过一个相关的错误。当时场景是这样的:

有一个微信公众号项目,调用微信公众号的接口都需要access_token,它的有效期是2小时。当时我的做法是把它存放在文件中,格式使用的是json。

{"access_token":"easWasdw32323", "expire":1588219064}

伪代码如下:

function getToken ($tokenFile){    $tokenJson = file_get_contents($tokenFile);    if (!$tokenJson) {        $token = loadToken($tokenFile);    } else if (json_decode($tokenJson, true)['expire'] <= time()){        $token = loadToken($tokenFile);    } else {        $token = json_decode($tokenJson, true)['access_token'];    }    return $token;}function loadToken ($tokenFile) {    $fp = fopen($tokenFile, 'r+');    $tokenJson = ...; // 调用微信接口获取到token    fwrite($fp, json_encode($tokenJson));    return $tokenJson['access_token'];}

出现的问题:

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

问题原因分析:

假设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

<?php $file = 'data.txt';$handler = fopen($file, 'a+') or die('文件资源打开失败');// 取得独占锁if (flock($handler, LOCK_EX)) {    sleep(5);    flock($handler, LOCK_UN);} else {    echo '锁定失败';}fclose($handler);demo2.php<?php $file = 'data.txt';$handler = fopen($file, 'a+') or die('文件资源打开失败');// 取得独占锁if (flock($handler, LOCK_EX)) {    fwrite($handler, 'sometest string');    flock($handler, LOCK_UN);} else {    echo '锁定失败';}fclose($handler);

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

解决问题

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

<?php function getToken ($tokenFile){    $tokenJson = file_get_contents($tokenFile);    if (!$tokenJson) {            $token = loadToken($tokenFile);    } else if (json_decode($tokenJson, true)['expire'] <= time()){           $token = loadToken($tokenFile);    } else {            $token = json_decode($tokenJson, true)['access_token'];    }    return $token;}function loadToken ($tokenFile) {    $fp = fopen($tokenFile, 'w');    // 取得独占锁    if (flock($fp, LOCK_EX)) {        $tokenJson = ...; // 调用微信接口获取到token        fwrite($fp, json_encode($tokenJson));        flock($fp, LOCK_UN);    } else {        return false;    }    return $tokenJson['access_token'];}

4e8dfe2e5458cdaa405f72e3831c85af.png

97ea23758f4c393fc2a39a882c838168.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值