ホームページ  >  記事  >  バックエンド開発  >  PHP ファイルロックによって引き起こされる問題と解決策 (実際のケース)

PHP ファイルロックによって引き起こされる問題と解決策 (実際のケース)

齐天大圣
齐天大圣オリジナル
2020-05-03 09:37:291905ブラウズ

実際のケース

私は以前に関連する間違いを犯したことを思い出しました。その時の状況は次のとおりです。WeChat パブリック アカウント プロジェクトがあり、WeChat パブリック アカウントを呼び出すためのインターフェイスには 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;];
}

発生する問題:

プロジェクトを一定期間実行すると問題が発生しますが、その後は正常になります。 1 ~ 2 秒後にリフレッシュされます。

問題の原因の分析:

トークンの有効期限が切れたと仮定すると、この時点で、それぞれ A と B という名前の 2 つのリクエストが来ます。 A が来ると、トークンの有効期限が切れた後、WeChat インターフェイスを呼び出して新しいトークンを取得し、取得後、トークンを保存するファイルを更新します。

しかし、ファイルが完全に更新されていないときに、B がトークンを格納したファイルを読みに来ました。トークン ファイル内のデータが完全に更新されていないため、B によって読み取られたデータはエラーを引き起こします。

さらに、A と B がファイルの内容を同時に更新している可能性があり、データの混乱が生じ、エラーが発生する可能性があります。

このエラーを回避するにはどうすればよいですか?

ファイルのロック機構を完成させることができます。

PHP では flock() 関数が提供されており、ファイルのロック メカニズム (ファイルのロックまたは解放) を使用できます。プロセスがファイルにアクセスするときにファイルをロックすると、他のプロセスがそのファイルにアクセスする場合は、ロックが解除されるまで待つ必要があります。これにより、同じファイルへの同時アクセス時のデータ破損が回避されます。

関数のプロトタイプは次のとおりです:

flock ( resource $handle , int $operation [, int &$wouldblock ] ) : bool

  • handle:

    ファイル システム ポインターは、通常は fopen() によって作成されるリソースです。

  • ## 操作

## これは、次の値のいずれかになります:

(プログラムの読み取り)。

LOCK_EX 排他ロック(書き込まれたプログラム)を取得します。 '’ 's’ '' '' – ‐ ‐ ‐ ‐ - を使用して、ロックを解放します(共有または排他的かどうか)。

LOCK_NB 追加のロック (Windows はサポートされていません)。

    wouldblock
  • ロックがブロックされる場合 (EWOULDBLOCK エラー コード)、オプションの 3 番目のパラメータが設定されます「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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。