L'analyse du code source explique pourquoi Laravel exécute à plusieurs reprises la même tâche de file d'attente

jacklove
Libérer: 2023-04-02 13:10:02
original
1757 Les gens l'ont consulté

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;
Copier après la connexion

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);
 }
}
Copier après la connexion

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}
Copier après la connexion

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
Copier après la connexion

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é

Articles qui pourraient vous intéresser :

À propos de Laravel Redis Explication détaillée du problème de plusieurs processus prenant des files d'attente en même temps

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!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
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
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal