zoukankan      html  css  js  c++  java
  • 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);
    echo "finish";

    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);
    
    $contents = file_get_contents($file);
    echo $contents;

    先运行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'];
    
    }

    链接:https://mp.weixin.qq.com/s/sq4KLQISGoaO-riyRh6JMQ

  • 相关阅读:
    Spring bean的自动装配
    JSP三大指令
    JSP九大内置对象
    Java异常的捕获顺序(多个catch)
    Integer.parseInt(s)和Integer.valueOf(s)之间的区别
    mysql忘记密码(MySQL5.7)
    java的四种内部类
    内存泄露查询
    深度优先和广度优先比较
    循环队列
  • 原文地址:https://www.cnblogs.com/clubs/p/13381149.html
Copyright © 2011-2022 走看看