¿Cómo puedo recuperar una lista de tareas en una cola que aún no se han procesado?
¿Cómo puedo recuperar una lista de tareas en una cola que aún no se han procesado?
Respuestas:
EDITAR: Vea otras respuestas para obtener una lista de tareas en la cola.
Usted debe mirar aquí: Guía de apio - Inspección de trabajadores
Básicamente esto:
from celery.app.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()
Dependiendo de lo que quieras
i.reserved()
para obtener una lista de tareas en cola.
inspect(['celery@Flatty'])
. Enorme mejora de velocidad inspect()
.
si está usando rabbitMQ, use esto en la terminal:
sudo rabbitmqctl list_queues
imprimirá una lista de colas con varias tareas pendientes. por ejemplo:
Listing queues ...
0b27d8c59fba4974893ec22d478a7093 0
0e0a2da9828a48bc86fe993b210d984f 0
10@torob2.celery.pidbox 0
11926b79e30a4f0a9d95df61b6f402f7 0
15c036ad25884b82839495fb29bd6395 1
celerey_mail_worker@torob2.celery.pidbox 0
celery 166
celeryev.795ec5bb-a919-46a8-80c6-5d91d2fcf2aa 0
celeryev.faa4da32-a225-4f6c-be3b-d8814856d1b6 0
el número en la columna derecha es el número de tareas en la cola. arriba, la cola de apio tiene 166 tareas pendientes.
grep -e "^celery\s" | cut -f2
para extraer eso 166
si desea procesar ese número más tarde, por ejemplo, para las estadísticas.
Si no usa tareas priorizadas, esto es bastante simple si está usando Redis. Para que la tarea cuente:
redis-cli -h HOST -p PORT -n DATABASE_NUMBER llen QUEUE_NAME
Pero, las tareas priorizadas usan una clave diferente en redis , por lo que la imagen completa es un poco más complicada. La imagen completa es que necesita consultar redis para cada prioridad de la tarea. En python (y del proyecto Flower), esto se ve así:
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 desea obtener una tarea real, puede usar algo como:
redis-cli -h HOST -p PORT -n DATABASE_NUMBER lrange QUEUE_NAME 0 -1
A partir de ahí, deberá deserializar la lista devuelta. En mi caso pude lograr esto con algo como:
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']))
Solo tenga en cuenta que la deserialización puede tomar un momento, y deberá ajustar los comandos anteriores para trabajar con varias prioridades.
DATABASE_NUMBER
usado por defecto es 0
, y el QUEUE_NAME
es celery
, entonces redis-cli -n 0 llen celery
devolverá el número de mensajes en cola.
'{{{0}}}{1}{2}'
lugar de '{0}{1}{2}'
. Aparte de eso, ¡esto funciona perfectamente!
Para recuperar tareas del backend, use esto
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)
Si está utilizando Celery + Django, la forma más sencilla de inspeccionar tareas utilizando comandos directamente desde su terminal en su entorno virtual o utilizando una ruta completa al apio:
Doc : http://docs.celeryproject.org/en/latest/userguide/workers.html?highlight=revoke#inspecting-workers
$ celery inspect reserved
$ celery inspect active
$ celery inspect registered
$ celery inspect scheduled
Además, si está utilizando Celery + RabbitMQ , puede inspeccionar la lista de colas con el siguiente comando:
Más información : https://linux.die.net/man/1/rabbitmqctl
$ sudo rabbitmqctl list_queues
celery -A my_proj inspect reserved
Una solución de copiar y pegar para Redis con serialización 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
Funciona con Django. Simplemente no te olvides de cambiar yourproject.celery
.
body =
línea a body = pickle.loads(base64.b64decode(j['body']))
.
El módulo de inspección de apio parece ser consciente de las tareas desde la perspectiva de los trabajadores. Si desea ver los mensajes que están en la cola (aún no han sido extraídos por los trabajadores), sugiero utilizar pyrabbit , que puede interactuar con la API http de rabbitmq para recuperar todo tipo de información de la cola.
Puede encontrar un ejemplo aquí: Recupere la longitud de la cola con Celery (RabbitMQ, Django)
Creo que la única forma de obtener las tareas que están esperando es mantener una lista de las tareas que comenzó y dejar que la tarea se elimine de la lista cuando se inicie.
Con rabbitmqctl y list_queues puede obtener una visión general de cuántas tareas están esperando, pero no las tareas en sí: http://www.rabbitmq.com/man/rabbitmqctl.1.man.html
Si lo que desea incluye la tarea que se está procesando, pero aún no ha terminado, puede mantener una lista de sus tareas y verificar sus estados:
from tasks import add
result = add.delay(4, 4)
result.ready() # True if finished
O deja que Celery almacene los resultados con CELERY_RESULT_BACKEND y verifica cuáles de tus tareas no están allí.
Esto funcionó para mí en mi aplicación:
def get_celery_queue_active_jobs(queue_name):
connection = <CELERY_APP_INSTANCE>.connection()
try:
channel = connection.channel()
name, jobs, consumers = channel.queue_declare(queue=queue_name, passive=True)
active_jobs = []
def dump_message(message):
active_jobs.append(message.properties['application_headers']['task'])
channel.basic_consume(queue=queue_name, callback=dump_message)
for job in range(jobs):
connection.drain_events()
return active_jobs
finally:
connection.close()
active_jobs
habrá una lista de cadenas que corresponden a tareas en la cola.
No olvides cambiar CELERY_APP_INSTANCE por el tuyo.
Gracias a @ashish por señalarme en la dirección correcta con su respuesta aquí: https://stackoverflow.com/a/19465670/9843399
jobs
siempre es cero ... alguna idea?
Hasta donde yo sé, Celery no da API para examinar las tareas que están esperando en la cola. Esto es específico del corredor. Si usa Redis como intermediario para un ejemplo, entonces examinar las tareas que están esperando en la celery
cola (predeterminada) es tan simple como:
celery
lista en la lista (comando LRANGE para un ejemplo)Tenga en cuenta que estas son tareas EN ESPERA para ser elegidas por los trabajadores disponibles. Su clúster puede tener algunas tareas ejecutándose; esas no estarán en esta lista ya que ya se han seleccionado.
Llegué a la conclusión de que la mejor manera de obtener el número de trabajos en una cola es usar rabbitmqctl
como se ha sugerido varias veces aquí. Para permitir que cualquier usuario elegido ejecute el comando sudo
, seguí las instrucciones aquí (omití editar la parte del perfil, ya que no me importa escribir sudo antes del comando).
También agarré jamesc's grep
y un cut
fragmento y lo envolví en llamadas de subproceso.
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]))
Si controla el código de las tareas, puede solucionar el problema permitiendo que una tarea desencadene un reintento trivial la primera vez que se ejecuta y luego verificando inspect().reserved()
. El reintento registra la tarea con el resultado backend, y el apio puede ver eso. La tarea debe aceptar self
o context
como primer parámetro para que podamos acceder al recuento de reintentos.
@task(bind=True)
def mytask(self):
if self.request.retries == 0:
raise self.retry(exc=MyTrivialError(), countdown=1)
...
Esta solución es independiente del agente, es decir. No tiene que preocuparse de si está utilizando RabbitMQ o Redis para almacenar las tareas.
EDITAR: después de probar, he encontrado que esto es solo una solución parcial. El tamaño de reservado está limitado a la configuración de captación previa para el trabajador.
Con 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))
Tenga cuidado de cambiar my_proj
conyour_proj