Le service de file d'attente de Laravel fournit une API unifiée pour divers services de file d'attente en arrière-plan. L'article suivant vous présente, grâce à l'analyse du code source, les raisons pour lesquelles Laravel exécute à plusieurs reprises la même tâche de file d'attente. Pour plus de détails, amis dans. besoin peut s'y référer. Jetons un coup d'œil ci-dessous.
Préface
Le service de file d'attente de Laravel fournit une API unifiée pour divers services de file d'attente en arrière-plan. Les files d'attente permettent de retarder l'exécution de tâches chronophages, comme l'envoi d'un email. Cela peut réduire efficacement le temps de réponse aux demandes.
Problème trouvé
Utiliser Redis dans Laravel pour traiter les tâches de file d'attente Les fonctions fournies par le framework sont très puissantes, mais j'ai récemment rencontré. un problème, c'est-à-dire qu'une tâche est exécutée plusieurs fois. Pourquoi est-ce ?
Permettez-moi d'abord de parler de la raison :
Parce que dans Laravel, si une file d'attente (tâche ) le temps d'exécution est supérieur à Après 60 secondes, cela sera considéré comme un échec d'exécution et rejoindra la file d'attente, ce qui entraînera l'exécution répétée de la même tâche.
La logique de cette tâche est de transmettre le contenu aux utilisateurs. Les utilisateurs doivent être récupérés et parcourus en fonction du contenu de la file d'attente, et envoyés via l'interface HTTP du backend de requête. Par exemple, s'il y a 10 000 utilisateurs, si le nombre d'utilisateurs est important ou si la vitesse de traitement de l'interface n'est pas si rapide, le temps d'exécution sera certainement supérieur à 60 secondes, la tâche sera donc réajoutée à la file d'attente. La situation est encore pire. Si les tâches précédentes ne sont pas exécutées dans les 60 secondes, elles seront de nouveau ajoutées à la file d'attente, de sorte que la même tâche sera exécutée non seulement une, mais plusieurs fois.
Trouvons le coupable à partir du code source de Laravel.
Fichier de code source : supplier/laravel/framework/src/Illuminate/Queue/RedisQueue.php
/** * The expiration time of a job. * * @var int|null */ protected $expire = 60;
La variable membre $expire est une valeur fixe Laravel estime qu'une file d'attente doit être exécutée peu importe la durée que cela prend 60 secondes. Méthode pour obtenir la file d'attente :
public function pop($queue = null) { $original = $queue ?: $this->default; $queue = $this->getQueue($queue); $this->migrateExpiredJobs($queue.':delayed', $queue); if (! is_null($this->expire)) { $this->migrateExpiredJobs($queue.':reserved', $queue); } list($job, $reserved) = $this->getConnection()->eval( LuaScripts::pop(), 2, $queue, $queue.':reserved', $this->getTime() + $this->expire ); if ($reserved) { return new RedisJob($this->container, $this, $job, $reserved, $original); } }
Il y a plusieurs étapes pour obtenir la file d'attente, car si l'exécution de la file d'attente échoue ou si l'exécution expire , il sera placé dans une autre collection. Enregistrez-le pour réessayer. Le processus est le suivant :
1. Repoussez la file d'attente qui a échoué en raison de l'exécution de la collection retardée vers la file d'attente actuellement exécutée. .
2. Repoussez la file d'attente en raison d'un délai d'exécution de la collection réservée vers la file d'attente actuellement exécutée.
3. Retirez ensuite la tâche de la file d'attente et commencez à l'exécuter, puis placez la file d'attente dans la collection ordonnée réservée.
La commande eval est utilisée pour exécuter ce processus, et plusieurs scripts Lua sont utilisés.
Récupérez la tâche de la file d'attente à exécuter :
local job = redis.call('lpop', KEYS[1]) local reserved = false if(job ~= false) then reserved = cjson.decode(job) reserved['attempts'] = reserved['attempts'] + 1 reserved = cjson.encode(reserved) redis.call('zadd', KEYS[2], ARGV[1], reserved) end return {job, reserved}
Vous pouvez voir que Laravel obtient la requête Redis Lors de l'exécution de la file d'attente, elle placera également une copie dans une collection ordonnée et utilisera l'horodatage d'expiration comme score.
Ce n'est que lorsque la tâche est terminée que la tâche sera supprimée de l'ensemble commandé. Le code permettant de supprimer la file d'attente de cette collection ordonnée est omis. Voyons comment Laravel gère les files d'attente dont le temps d'exécution est supérieur à 60 secondes.
C'est ce que fait ce script Lua :
local val = redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1]) if(next(val) ~= nil) then redis.call('zremrangebyrank', KEYS[1], 0, #val - 1) for i = 1, #val, 100 do redis.call('rpush', KEYS[2], unpack(val, i, math.min(i+99, #val))) end end return true
Ici, zrangebyscore découvre le score d'Elements comme petit comme l'horodatage actuel, c'est-à-dire que les tâches ajoutées à l'ensemble il y a 60 secondes, sont ensuite supprimées de l'ensemble via zremrangebyrank et repoussées vers la file d'attente.
Vous devriez avoir une prise de conscience soudaine après avoir vu cela.
Si une file d'attente n'est pas exécutée dans les 60 secondes, le processus repoussera à nouveau les tâches de l'ensemble réservé vers la file d'attente lors de la récupération de la file d'attente.
Résumé
Explication détaillée du code source de php-msf
URL et routage thinkphp5 Explication détaillée des fonctions et exemples
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!