Recupera l'elenco delle attività in una coda in Sedano


147

Come posso recuperare un elenco di attività in una coda che devono ancora essere elaborate?


1
RabbitMQ, ma voglio recuperare questo elenco all'interno di Python.
bradley.ayers,

Risposte:


174

EDIT: vedi altre risposte per ottenere un elenco di attività in coda.

Dovresti guardare qui: Guida al sedano - Ispezionando i lavoratori

Fondamentalmente questo:

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()

A seconda di cosa vuoi


9
L'ho provato, ma è davvero lento (come 1 secondo). Lo sto usando in modo sincrono in un'app tornado per monitorare i progressi, quindi deve essere veloce.
JulienFr

41
Ciò non restituirà un elenco di attività nella coda che devono ancora essere elaborate.
Ed J,

9
Utilizzare i.reserved()per ottenere un elenco di attività in coda.
Banana,

4
Qualcuno ha sperimentato che i.reserved () non avrà un elenco accurato di attività attive? Ho delle attività in esecuzione che non compaiono nell'elenco. Sono su django-sedano == 3.1.10
Seperman,

6
Quando si specifica l'operaio ho dovuto usare una lista come argomento: inspect(['celery@Flatty']). Enorme miglioramento della velocità inspect().
Adversus,

42

se stai usando rabbitMQ, usa questo nel terminale:

sudo rabbitmqctl list_queues

stamperà un elenco di code con il numero di attività in sospeso. per esempio:

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

il numero nella colonna di destra è il numero di attività nella coda. sopra, la coda di sedano ha 166 attività in sospeso.


1
Conosco questo quando ho i privilegi di sudo, ma voglio che un utente di sistema non privilegiato sia in grado di verificare - qualche suggerimento?
saggio,

Inoltre è possibile reindirizzare questo grep -e "^celery\s" | cut -f2per estrarre che 166se si desidera elaborare quel numero in un secondo momento, dire statistiche.
Jamesc

22

Se non si utilizzano attività prioritarie, questo è piuttosto semplice se si utilizza Redis. Per ottenere il conteggio delle attività:

redis-cli -h HOST -p PORT -n DATABASE_NUMBER llen QUEUE_NAME

Tuttavia, le attività prioritarie utilizzano una chiave diversa in redis , quindi il quadro completo è leggermente più complicato. L'immagine completa è che è necessario interrogare redis per ogni priorità dell'attività. In python (e dal progetto Flower), questo assomiglia a:

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])

Se vuoi ottenere un vero compito, puoi usare qualcosa come:

redis-cli -h HOST -p PORT -n DATABASE_NUMBER lrange QUEUE_NAME 0 -1

Da lì dovrai deserializzare l'elenco restituito. Nel mio caso sono stato in grado di ottenere questo risultato con qualcosa del tipo:

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']))

Basta essere avvisati che la deserializzazione può richiedere un momento e dovrai adattare i comandi sopra per lavorare con varie priorità.


Dopo averlo usato in produzione, ho imparato che fallisce se si utilizzano attività prioritarie , a causa del design di Sedano.
mlissner,

1
Ho aggiornato quanto sopra per gestire le attività prioritarie. Progresso!
mlissner,

1
Solo per precisare le cose, l' DATABASE_NUMBERusato di default è 0e l' QUEUE_NAMEè celery, quindi redis-cli -n 0 llen celeryrestituirà il numero di messaggi in coda.
Vineet Bansal,

Per il mio sedano, il nome della coda è '{{{0}}}{1}{2}'invece di '{0}{1}{2}'. A parte questo, funziona perfettamente!
zupo

12

Per recuperare attività dal back-end, utilizzare questo

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)

2
ma "jobs" fornisce solo un numero di attività in coda
bitnik

Vedere stackoverflow.com/a/57807913/9843399 per la risposta correlata che fornisce i nomi delle attività.
Caleb Syring,

10

Se stai usando Celery + Django il modo più semplice per ispezionare le attività usando i comandi direttamente dal tuo terminale nel tuo ambiente virtuale o usando un percorso completo per il sedano:

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

Inoltre, se stai usando Celery + RabbitMQ puoi controllare l'elenco delle code usando il seguente comando:

Maggiori informazioni : https://linux.die.net/man/1/rabbitmqctl

$ sudo rabbitmqctl list_queues

4
Se hai un progetto definito, puoi usarecelery -A my_proj inspect reserved
sashaboulouds il

6

Una soluzione copia-incolla per Redis con serializzazione 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

Funziona con Django. Basta non dimenticare di cambiare yourproject.celery.


1
Se stai usando il serializzatore sottaceto, puoi cambiare la body =linea in body = pickle.loads(base64.b64decode(j['body'])).
Jim Hunziker,

4

Il modulo di ispezione del sedano sembra essere consapevole solo dei compiti dal punto di vista dei lavoratori. Se si desidera visualizzare i messaggi in coda (che devono ancora essere estratti dai lavoratori), suggerisco di usare pyrabbit , che può interfacciarsi con l'api http di rabbitmq per recuperare tutti i tipi di informazioni dalla coda.

Un esempio può essere trovato qui: Recupera la lunghezza della coda con Sedano (RabbitMQ, Django)


3

Penso che l'unico modo per ottenere le attività in attesa sia quello di mantenere un elenco di attività avviate e lasciare che l'attività si rimuova dall'elenco quando viene avviato.

Con rabbitmqctl e list_queues è possibile ottenere una panoramica di quante attività sono in attesa, ma non delle attività stesse: http://www.rabbitmq.com/man/rabbitmqctl.1.man.html

Se ciò che desideri include l'attività in fase di elaborazione, ma non è ancora terminata, puoi tenere un elenco delle attività e verificarne gli stati:

from tasks import add
result = add.delay(4, 4)

result.ready() # True if finished

Oppure lasci che Celery memorizzi i risultati con CELERY_RESULT_BACKEND e controlli quali attività non sono presenti.


3

Questo ha funzionato per me nella mia applicazione:

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 sarà un elenco di stringhe che corrispondono alle attività in coda.

Non dimenticare di scambiare CELERY_APP_INSTANCE con il tuo.

Grazie a @ashish per avermi indicato nella giusta direzione con la sua risposta qui: https://stackoverflow.com/a/19465670/9843399


nel mio caso jobsè sempre zero ... qualche idea?
daveoncode

@daveoncode Non credo che siano sufficienti informazioni per rispondere in modo utile. Potresti aprire la tua domanda. Non penso che sarebbe un duplicato di questo se si specifica che si desidera recuperare le informazioni in Python. Ritornerei su stackoverflow.com/a/19465670/9843399 , che è ciò su cui ho basato la mia risposta, e mi assicurerei che funzioni prima.
Caleb Syring,

@CalebSyring Questo è il primo approccio che mi mostra davvero le attività in coda. Molto bella. L'unico problema per me è che l'appendice dell'elenco non sembra funzionare. Qualche idea su come posso fare in modo che la funzione di callback scriva nell'elenco?
Varlor,

@Varlor Mi dispiace, qualcuno ha apportato una modifica impropria alla mia risposta. Puoi cercare la risposta originale nella cronologia delle modifiche, che molto probabilmente funzionerà per te. Sto lavorando per risolverlo. (EDIT: Sono appena entrato e ho rifiutato la modifica, che aveva un evidente errore di pitone. Fammi sapere se questo ha risolto il tuo problema o no.)
Caleb Syring

@CalebSyring Ora ho usato il tuo codice in una classe, avendo l'elenco come attributo di classe funziona!
Varlor,

2

Per quanto ne so, il sedano non fornisce l'API per l'esame delle attività in attesa in coda. Questo è specifico del broker. Se usi Redis come broker per un esempio, esaminare le attività in attesa nella celerycoda (predefinita) è semplice come:

  1. connettersi al database del broker
  2. elenca gli elementi celerynell'elenco (comando LRANGE per un esempio)

Tieni presente che si tratta di attività IN ATTESA di essere raccolte dai lavoratori disponibili. Il cluster potrebbe avere alcune attività in esecuzione: quelle non saranno in questo elenco poiché sono già state selezionate.


1

Sono giunto alla conclusione che il modo migliore per ottenere il numero di lavori su una coda è utilizzare rabbitmqctlcome è stato suggerito più volte qui. Per consentire a qualsiasi utente scelto di eseguire il comando con sudoho seguito le istruzioni qui (ho saltato la modifica della parte del profilo poiché non mi dispiace digitare in sudo prima del comando.)

Ho anche preso jamesc grepe cutsnippet e lo ho avvolto in chiamate di sottoprocesso.

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]))

1
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

0

Se controlli il codice delle attività, puoi aggirare il problema lasciando che un'attività attivi un nuovo tentativo banale la prima volta che viene eseguita, quindi controllando inspect().reserved(). Il nuovo tentativo registra l'attività con il risultato backend e il sedano può vederlo. L'attività deve accettare selfo contextcome primo parametro in modo da poter accedere al conteggio dei tentativi.

@task(bind=True)
def mytask(self):
    if self.request.retries == 0:
        raise self.retry(exc=MyTrivialError(), countdown=1)
    ...

Questa soluzione è agnostica del broker, vale a dire. non devi preoccuparti se stai usando RabbitMQ o Redis per archiviare le attività.

EDIT: dopo i test ho scoperto che questa è solo una soluzione parziale. La dimensione di riservato è limitata all'impostazione di prefetch per il lavoratore.


0

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))

Fai attenzione a cambiare my_projconyour_proj

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.