Maison  >  Article  >  développement back-end  >  Que dois-je faire si le verrouillage php du troupeau échoue ?

Que dois-je faire si le verrouillage php du troupeau échoue ?

藏色散人
藏色散人original
2021-11-08 09:32:402233parcourir

L'échec du verrouillage PHP de flock est dû au fait que $file_lock n'est pas utilisé après la sortie de la méthode isRunning(). La solution consiste à garantir que le descripteur de fichier ne sera pas libéré pendant tout le cycle de vie de PHP.

Que dois-je faire si le verrouillage php du troupeau échoue ?

L'environnement d'exploitation de cet article : système Windows 7, PHP version 7.1, ordinateur DELL G3

Que dois-je faire si le verrouillage php échoue ?

Solution au problème d'échec du flock php :

Au cours des deux derniers jours, j'ai écrit une méthode pour mon projet parallèle afin d'éviter l'exécution simultanée de scripts PHP planifiés par crontab.

Comment faire

Généralement, en utilisant la méthode file lock flock, le même script PHP verrouille le même fichier disque de manière non bloquante. Si le fichier est occupé, une erreur sera signalée, afin que le script puisse. sortez immédiatement.

Phénomène

Mais en pratique, on constate qu'il est possible de flocker directement dans le fichier du contrôleur, mais lorsque la logique du flock est encapsulée dans une fonction dans d'autres fichiers, elle devient invalide.

Cause

Après un long débogage, je me suis soudain rappelé que j'avais déjà rencontré ce piège. .

Le code d'erreur est le suivant :

class Crontab
{
    /**
     * 确保任务没有并发执行
     */
    public static function isRunning() {
        global $argv;
        $ident = [];
        foreach ($argv as $idx => $value) {
            $ident[] = $idx . '=' . urlencode($value);
        }
        $ident = md5(implode('&', $ident));
        $lockDir = \Yii::getAlias('@app/runtime/crontab/');
        @mkdir($lockDir, 0755, true);
        $file_lock = fopen($lockDir . $ident, 'w+');
        $wouldBlock = 0;
        flock($file_lock, LOCK_EX | LOCK_NB, $wouldBlock);
        return $wouldBlock;
    }
}
class Crontab
{
    /**
     * 确保任务没有并发执行
     */
    public static function isRunning() {
        global $argv;
 
        $ident = [];
        foreach ($argv as $idx => $value) {
            $ident[] = $idx . '=' . urlencode($value);
        }
        $ident = md5(implode('&', $ident));
 
        $lockDir = \Yii::getAlias('@app/runtime/crontab/');
 
        @mkdir($lockDir, 0755, true);
 
        $file_lock = fopen($lockDir . $ident, 'w+');
 
        $wouldBlock = 0;
        flock($file_lock, LOCK_EX | LOCK_NB, $wouldBlock);
 
        return $wouldBlock;
    }
}

Générez une valeur de hachage unique basée sur les paramètres de ligne de commande, représentant la tâche PHP.

Créez un fichier de verrouillage, exécutez le verrouillage non bloquant flock et renvoyez willBlock pour identifier si le verrou a été occupé.

J'ai appelé la méthode Crontab::isRunning() à l'entrée du script et j'ai découvert qu'après avoir démarré le script simultanément, le verrou peut toujours être obtenu.

La raison de l'erreur est la suivante : après la sortie de la méthode isRunning(), $file_lock n'est plus utilisé et est récupéré par PHP. Le descripteur de fichier $fp est fermé, ce qui entraîne la libération automatique du verrou.

Solution

class Crontab
{
    /**
     * 保存起来避免被php作为垃圾回收
     * @var null
     */
    static $file_lock = null;
    /**
     * 确保任务没有并发执行
     */
    public static function isRunning() {
        global $argv;
        $ident = [];
        foreach ($argv as $idx => $value) {
            $ident[] = $idx . '=' . urlencode($value);
        }
        $ident = md5(implode('&', $ident));
        $lockDir = \Yii::getAlias('@app/runtime/crontab/');
        @mkdir($lockDir, 0755, true);
        self::$file_lock = fopen($lockDir . $ident, 'w+');
        $wouldBlock = 0;
        flock(self::$file_lock, LOCK_EX | LOCK_NB, $wouldBlock);
        return $wouldBlock;
    }
}
class Crontab
{
    /**
     * 保存起来避免被php作为垃圾回收
     * @var null
     */
    static $file_lock = null;
 
    /**
     * 确保任务没有并发执行
     */
    public static function isRunning() {
        global $argv;
 
        $ident = [];
        foreach ($argv as $idx => $value) {
            $ident[] = $idx . '=' . urlencode($value);
        }
        $ident = md5(implode('&', $ident));
 
        $lockDir = \Yii::getAlias('@app/runtime/crontab/');
 
        @mkdir($lockDir, 0755, true);
 
        self::$file_lock = fopen($lockDir . $ident, 'w+');
 
        $wouldBlock = 0;
        flock(self::$file_lock, LOCK_EX | LOCK_NB, $wouldBlock);
 
        return $wouldBlock;
    }
}

Assurez-vous simplement que le descripteur de fichier ne sera pas publié pendant tout le cycle de vie de PHP, il est donc enregistré dans la variable membre statique de la classe.

Apprentissage recommandé : "Tutoriel vidéo PHP"

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