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

  • 相关阅读:
    形象理解ERP(转)
    禁用windows server 2008 域密码复杂性要求策略
    How to adding find,filter,remove filter on display method Form
    Windows Server 2008 R2激活工具
    How to using bat command running VS development SSRS report
    Creating Your First Mac AppGetting Started
    Creating Your First Mac AppAdding a Track Object 添加一个 Track 对象
    Creating Your First Mac AppImplementing Action Methods 实现动作方法
    Creating Your First Mac AppReviewing the Code 审查代码
    Creating Your First Mac AppConfiguring the window 设置窗口
  • 原文地址:https://www.cnblogs.com/clubs/p/13381149.html
Copyright © 2011-2022 走看看