Comment puis-je récupérer une liste de tâches dans une file d'attente qui doivent encore être traitées?
EDIT: Voir les autres réponses pour obtenir une liste de tâches dans la file d'attente.
Vous devriez regarder ici: Guide de céleri - Inspecter les travailleurs
Fondamentalement ceci:
>>> from celery.task.control import inspect
# Inspect all nodes.
>>> i = inspect()
# Show the items that have an ETA or are scheduled for later processing
>>> i.scheduled()
# Show tasks that are currently active.
>>> i.active()
# Show tasks that have been claimed by workers
>>> i.reserved()
En fonction de ce que vous voulez
si vous utilisez rabbitMQ, utilisez ceci dans terminal:
Sudo rabbitmqctl list_queues
il imprimera la liste des files d'attente avec le nombre de tâches en attente. par exemple:
Listing queues ...
0b27d8c59fba4974893ec22d478a7093 0
0e0a2da9828a48bc86fe993b210d984f 0
[email protected] 0
11926b79e30a4f0a9d95df61b6f402f7 0
15c036ad25884b82839495fb29bd6395 1
[email protected] 0
celery 166
celeryev.795ec5bb-a919-46a8-80c6-5d91d2fcf2aa 0
celeryev.faa4da32-a225-4f6c-be3b-d8814856d1b6 0
le nombre dans la colonne de droite est le nombre de tâches dans la file d'attente. ci-dessus, la file de céleri a 166 tâches en attente.
Si vous n'utilisez pas de tâches hiérarchisées, c'est assez simple si vous utilisez Redis. Pour que la tâche compte:
redis-cli -h Host -p PORT -n DATABASE_NUMBER llen QUEUE_NAME
Cependant, les tâches classées par ordre de priorité utilisent une clé différente dans redis ; L'image complète est que vous devez interroger redis pour chaque priorité de tâche. En python (et du projet Flower), cela ressemble à:
PRIORITY_SEP = '\x06\x16'
DEFAULT_PRIORITY_STEPS = [0, 3, 6, 9]
def make_queue_name_for_pri(queue, pri):
"""Make a queue name for redis
Celery uses PRIORITY_SEP to separate different priorities of tasks into
different queues in Redis. Each queue-priority combination becomes a key in
redis with names like:
- batch1\x06\x163 <-- P3 queue named batch1
There's more information about this in Github, but it doesn't look like it
will change any time soon:
- https://github.com/celery/kombu/issues/422
In that ticket the code below, from the Flower project, is referenced:
- https://github.com/mher/flower/blob/master/flower/utils/broker.py#L135
:param queue: The name of the queue to make a name for.
:param pri: The priority to make a name with.
:return: A name for the queue-priority pair.
"""
if pri not in DEFAULT_PRIORITY_STEPS:
raise ValueError('Priority not in priority steps')
return '{0}{1}{2}'.format(*((queue, PRIORITY_SEP, pri) if pri else
(queue, '', '')))
def get_queue_length(queue_name='celery'):
"""Get the number of tasks in a celery queue.
:param queue_name: The name of the queue you want to inspect.
:return: the number of items in the queue.
"""
priority_names = [make_queue_name_for_pri(queue_name, pri) for pri in
DEFAULT_PRIORITY_STEPS]
r = redis.StrictRedis(
Host=settings.REDIS_Host,
port=settings.REDIS_PORT,
db=settings.REDIS_DATABASES['CELERY'],
)
return sum([r.llen(x) for x in priority_names])
Si vous voulez obtenir une tâche réelle, vous pouvez utiliser quelque chose comme:
redis-cli -h Host -p PORT -n DATABASE_NUMBER lrange QUEUE_NAME 0 -1
De là, vous devrez désérialiser la liste renvoyée. Dans mon cas, j'ai pu accomplir cela avec quelque chose comme:
r = redis.StrictRedis(
Host=settings.REDIS_Host,
port=settings.REDIS_PORT,
db=settings.REDIS_DATABASES['CELERY'],
)
l = r.lrange('celery', 0, -1)
pickle.loads(base64.decodestring(json.loads(l[0])['body']))
Sachez simplement que la désérialisation peut prendre un moment, et que vous aurez besoin d’ajuster les commandes ci-dessus pour gérer différentes priorités.
Pour récupérer des tâches depuis le backend, utilisez cette
from amqplib import client_0_8 as amqp
conn = amqp.Connection(Host="localhost:5672 ", userid="guest",
password="guest", virtual_Host="/", insist=False)
chan = conn.channel()
name, jobs, consumers = chan.queue_declare(queue="queue_name", passive=True)
Le module d’inspection du céleri semble ne connaître que les tâches du point de vue des travailleurs. Si vous voulez voir les messages qui sont dans la file (que les travailleurs doivent encore extraire), je suggère d'utiliser pyrabbit , qui peut s’interfacer avec l’API http Rabbitmq pour récupérer toutes sortes d’informations dans la file.
Un exemple peut être trouvé ici: Récupérer la longueur de la file d’attente avec Celery (RabbitMQ, Django)
Une solution copier/coller pour Redis avec sérialisation json:
def get_celery_queue_items(queue_name):
import base64
import json
# Get a configured instance of a celery app:
from yourproject.celery import app as celery_app
with celery_app.pool.acquire(block=True) as conn:
tasks = conn.default_channel.client.lrange(queue_name, 0, -1)
decoded_tasks = []
for task in tasks:
j = json.loads(task)
body = json.loads(base64.b64decode(j['body']))
decoded_tasks.append(body)
return decoded_tasks
Cela fonctionne avec Django. N'oubliez pas de changer yourproject.celery
.
Je pense que le seul moyen d'obtenir les tâches en attente est de conserver une liste des tâches que vous avez commencées et de laisser la tâche se supprimer d'elle-même au début.
Avec rabbitmqctl et list_queues, vous pouvez avoir un aperçu du nombre de tâches en attente, mais pas des tâches proprement dites: http://www.rabbitmq.com/man/rabbitmqctl.1.man.html
Si ce que vous voulez inclut la tâche en cours de traitement, mais ne sont pas encore terminés, vous pouvez conserver une liste de vos tâches et vérifier leur état:
from tasks import add
result = add.delay(4, 4)
result.ready() # True if finished
Vous pouvez également laisser Celery stocker les résultats avec CELERY_RESULT_BACKEND et vérifier quelles tâches ne figurent pas dans cette liste.
Pour autant que je sache, Celery ne fournit pas d'API pour examiner les tâches en attente. Ceci est spécifique au courtier. Si vous utilisez Redis comme exemple de courtier, l'examen des tâches en attente dans la file d'attente celery
(par défaut) est aussi simple que:
celery
(commande LRANGE pour un exemple)N'oubliez pas que ces tâches doivent être sélectionnées par les employés disponibles. Votre cluster peut avoir certaines tâches en cours d'exécution - celles-ci ne seront pas dans cette liste car elles ont déjà été sélectionnées.
from celery.task.control import inspect
def key_in_list(k, l):
return bool([True for i in l if k in i.values()])
def check_task(task_id):
task_value_dict = inspect().active().values()
for task_list in task_value_dict:
if self.key_in_list(task_id, task_list):
return True
return False
Si vous utilisez Celery + Django le moyen le plus simple d'inspecter des tâches à l'aide de commandes directement à partir de votre terminal dans votre environnement virtuel ou à l'aide d'un chemin complet de céleri
Doc: http://docs.celeryproject.org/en/latest/userguide/workers.html?highlight=revoke#inspecting-workers
$ celery inspect reserved
$ celery inspect inspect
$ celery inspect registered
$ celery inspect scheduled
De même, si vous utilisez Celery + RabbitMQ vous pouvez inspectez la liste des files d'attente à l'aide de la commande suivante:
Plus d'info: https://linux.die.net/man/1/rabbitmqctl
$ Sudo rabbitmqctl list_queues
J'en suis venu à la conclusion que le meilleur moyen d'obtenir le nombre de travaux en file d'attente est d'utiliser rabbitmqctl
comme cela a été suggéré à plusieurs reprises ici. Pour permettre à n'importe quel utilisateur choisi d'exécuter la commande avec Sudo
, j'ai suivi les instructions here (j'ai sauté la modification de la partie du profil car cela ne me dérange pas de taper Sudo avant la commande.)
J'ai également récupéré les extraits grep
et cut
de jamesc et les ai intégrés à des appels de sous-processus.
from subprocess import Popen, PIPE
p1 = Popen(["Sudo", "rabbitmqctl", "list_queues", "-p", "[name of your virtula Host"], stdout=PIPE)
p2 = Popen(["grep", "-e", "^celery\s"], stdin=p1.stdout, stdout=PIPE)
p3 = Popen(["cut", "-f2"], stdin=p2.stdout, stdout=PIPE)
p1.stdout.close()
p2.stdout.close()
print("number of jobs on queue: %i" % int(p3.communicate()[0]))
Avec subprocess.run
:
import subprocess
import re
active_process_txt = subprocess.run(['celery', '-A', 'my_proj', 'inspect', 'active'],
stdout=subprocess.PIPE).stdout.decode('utf-8')
return len(re.findall(r'worker_pid', active_process_txt))
Veillez à changer my_proj
avec your_proj
Si vous contrôlez le code des tâches, vous pouvez contourner le problème en laissant une tâche déclencher une nouvelle tentative triviale lors de la première exécution, puis en vérifiant inspect().reserved()
. La nouvelle tentative enregistre la tâche avec le résultat final et le céleri peut le voir. La tâche doit accepter self
ou context
en tant que premier paramètre pour que nous puissions accéder au nombre de tentatives.
@task(bind=True)
def mytask(self):
if self.request.retries == 0:
raise self.retry(exc=MyTrivialError(), countdown=1)
...
Cette solution est agnostique auprès des courtiers, c.-à-d. vous n'avez pas à vous demander si vous utilisez RabbitMQ ou Redis pour stocker les tâches.
EDIT: après les tests, j'ai trouvé que ce n'était qu'une solution partielle. La taille de reserved est limitée au paramètre de prélecture pour le travailleur.
lancement de la fleur - spectateur des tâches de selery
celery -A app.celery flower
puis ouvrez dans le navigateur
localhost:5555