1. 程式人生 > 實用技巧 >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);
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