首頁  >  文章  >  後端開發  >  php檔案鎖產生的問題與解決方案(一個真實案例)

php檔案鎖產生的問題與解決方案(一個真實案例)

齐天大圣
齐天大圣原創
2020-05-03 09:37:291886瀏覽

一個真實案例

想起自己之前犯過一個相關的錯誤。當時場景是這樣的:有一個微信公眾號項目,呼叫微信公眾號的介面都需要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)[&#39;expire&#39;] <= time()){
        $token = loadToken($tokenFile);
    } else {
        $token = json_decode($tokenJson, true)[&#39;access_token&#39;];
    }
    
    return $token;
}
function loadToken ($tokenFile) 
{
    $fp = fopen($tokenFile, &#39;r+&#39;);
    
    $tokenJson = ...; // 调用微信接口获取到token
    fwrite($fp, json_encode($tokenJson));
    
    return $tokenJson[&#39;access_token&#39;];
}

出現的問題:

專案運行一段時間後就會出問題,但過一、兩秒後再刷新就正常了。

問題原因分析:

假設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_EX 為專屬鎖定時所鎖定(寫入的程序)。                 

#        LOCK_UN 釋放時所鎖定(不論共用或專屬)。                       

            LOCK_NB在附加鎖定時所鎖定(Windows 或未支援)。
  • wouldblock

        如果鎖定會阻塞的話(EWOULDBLOCK    錯誤碼情況下),可選擇的第三個參數會被設定為TRUE。 (Windows 上不支援)

demo

demo1.php

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

demo2.php

<?php
$file = &#39;data.txt&#39;;
$handler = fopen($file, &#39;a+&#39;) or die(&#39;文件资源打开失败&#39;);
 
// 取得独占锁
if (flock($handler, LOCK_EX)) {
    fwrite($handler, &#39;sometest string&#39;);
    flock($handler, LOCK_UN);
} else {
    echo &#39;锁定失败&#39;;
}
 
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)[&#39;expire&#39;] <= time()){ 
           $token = loadToken($tokenFile);
    } else {
            $token = json_decode($tokenJson, true)[&#39;access_token&#39;];    
    }
    return $token;
}

function loadToken ($tokenFile) {
    $fp = fopen($tokenFile, &#39;w&#39;);    // 取得独占锁    
    if (flock($fp, LOCK_EX)) {
        $tokenJson = ...; // 调用微信接口获取到token     
        fwrite($fp, json_encode($tokenJson)); 
        flock($fp, LOCK_UN);    
    } else {
        return false;    
    }
    
    return $tokenJson[&#39;access_token&#39;];
}

以上是php檔案鎖產生的問題與解決方案(一個真實案例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn