Maison  >  Article  >  développement back-end  >  Problèmes et solutions causés par le verrouillage de fichiers PHP (un cas réel)

Problèmes et solutions causés par le verrouillage de fichiers PHP (un cas réel)

齐天大圣
齐天大圣original
2020-05-03 09:37:291892parcourir

Un cas réel

Je me suis souvenu que j'avais déjà commis une erreur connexe. La scène à cette époque était la suivante : il y avait un projet de compte public WeChat, et l'interface pour appeler le compte public WeChat était requise access_token, et sa durée de validité est de 2 heures. Ce que j'ai fait à cette époque, c'était de le stocker dans un fichier, en utilisant le format json. {"access_token":"easWasdw32323", "expire":1588219064}. Le pseudocode est le suivant :

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;];
}

Problèmes :

Des problèmes surviendront après l'exécution du projet pendant un certain temps, mais ce sera normal après une actualisation après une seconde ou deux .

Analyse de la cause du problème :

Supposons que le token ait expiré A ce moment, deux requêtes arrivent, nommées respectivement A et B. Lorsque A arrive, il constate qu'après l'expiration du jeton, il appelle l'interface WeChat pour obtenir un nouveau jeton. Après l'avoir obtenu, il le met à jour dans le fichier stockant le jeton.

Cependant, lorsque le fichier n'a pas été complètement mis à jour, B est venu lire le fichier stockant le jeton. Étant donné que les données du fichier de jetons ne sont pas complètement mises à jour, les données lues par B provoqueront des erreurs.

De plus, il est possible que A et B mettent à jour le contenu du fichier en même temps, ce qui entraînerait une confusion des données et entraînerait des erreurs.

Comment éviter cette erreur ?

Le mécanisme de verrouillage des fichiers peut être complété.

La fonction flock() est fournie en PHP, qui permet d'utiliser le mécanisme de verrouillage (verrouiller ou libérer le fichier) sur le fichier. Lorsqu'un processus verrouille un fichier lors de son accès, les autres processus doivent attendre que le verrou soit libéré s'ils souhaitent accéder au fichier. Cela évite la corruption des données lors de l'accès simultané au même fichier.

Le prototype de la fonction est le suivant :

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

  • handle :

    Le pointeur du système de fichiers est généralement utilisé par la ressource fopen () créée.

  • opération

opération peut être l'une des valeurs suivantes :

LOCK_SH acquiert le verrou partagé (programme de lecture).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                

Verrou supplémentaire LOCK_NB (Windows n'est pas pris en charge).

  • Si le verrou est bloqué (code d'erreur EWOULDBLOCK), le troisième paramètre facultatif qui sera défini est TRUE. (Non pris en charge sous Windows) wouldblock

    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);

Exécutez d'abord demo1.php, puis exécutez demo2.php immédiatement, vous constaterez que, comme le fichier est verrouillé par demo1.php, demo2.php ne peut pas écrire de nouveau contenu. Ce n'est qu'après que demo1.php a libéré le verrou que demo2.php peut obtenir le verrou exclusif, puis écrire le fichier.

Résoudre des problèmes

Après avoir acquis ces connaissances, je serai capable de résoudre mes problèmes précédents. Le code amélioré est le suivant :

<?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;];
}

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn