Отримайте список завдань у черзі в селері


147

Як я можу отримати список завдань у черзі, які ще обробляються?


1
RabbitMQ, але я хочу отримати цей список всередині Python.
— bradley.ayers

Відповіді:


174

РЕДАКТУВАННЯ: Див. Інші відповіді для отримання списку завдань у черзі.

Ви повинні подивитися тут: Посібник із селери - Огляд робітників

В основному це:

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

Залежно від того, що ви хочете


9
Я спробував це, але це дуже повільно (як 1 сек). Я використовую це синхронічно в додатку "Торнадо" для моніторингу прогресу, тому він повинен бути швидким.
— JulienFr

41
Це не поверне список завдань у черзі, які ще обробляються.
— Ред J

9
Використовуйте i.reserved()для отримання списку завдань у черзі.
— Банан

4
Хто-небудь відчував, що i.reserved () не матиме точного списку активних завдань? У мене запущені завдання, які не відображаються у списку. Я на джанго-селері == 3.1.10
— Сепермен

6
При вказівці працівника я повинен був використовувати список в якості аргументу: inspect(['celery@Flatty']). Величезне покращення швидкості з часом inspect().
— Adversus

42

якщо ви використовуєте rabbitMQ, використовуйте це в терміналі:

sudo rabbitmqctl list_queues

він надрукує список черг із кількістю завдань, що очікують на розгляд. наприклад:

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

число в правій колонці - це кількість завдань у черзі. вище, у черзі селери є 166 очікуючих завдань.


1
Мені це знайоме, коли у мене є привілеї sudo, але я хочу, щоб непривілейований користувач системи міг перевірити - будь-які пропозиції?
— шавлія

Крім того, ви можете grep -e "^celery\s" | cut -f2передати це, щоб витягти, що, 166якщо ви хочете обробити це число пізніше, скажімо, для статистики.
— jamesc

21

Якщо ви не використовуєте пріоритетні завдання, це насправді досить просто, якщо ви використовуєте Redis. Щоб отримати підрахунок завдання:

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

Але в пріоритетних завданнях використовується інша клавіша в redis , тому повна картина трохи складніше. Повна картина полягає в тому, що вам потрібно запитувати redis для кожного пріоритетного завдання. У python (і в проекті Flower) це виглядає так:

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

Якщо ви хочете отримати власне завдання, ви можете використовувати щось на кшталт:

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

Звідти вам доведеться десаріалізувати повернений список. У моєму випадку мені вдалося досягти цього чимось на кшталт:

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

Зауважте, що дезаріалізація може зайняти мить, і вам потрібно буде скоригувати команди вище, щоб працювати з різними пріоритетами.


Використовуючи це у виробництві, я дізнався, що він не вдається, якщо використовувати пріоритетні завдання , завдяки дизайну селери.
— mlissner

1
Я оновив вище, щоб вирішити пріоритетні завдання. Прогрес!
— mlissner

1
Тільки для написання речей, DATABASE_NUMBERза замовчуванням використовується те, що є 0, і QUEUE_NAMEє celery, тому redis-cli -n 0 llen celeryповерне кількість повідомлень у черзі.
— Bineal Vineet

Для моєї селери, '{{{0}}}{1}{2}'замість цього , назва черги '{0}{1}{2}'. Крім цього, це прекрасно працює!
— zupo

12

Щоб отримати завдання із бекенда, використовуйте це

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
але "робочі місця" дає лише кілька завдань у черзі
— бітник

Дивіться stackoverflow.com/a/57807913/9843399 для відповідної відповіді, яка дає назви завдань.
— Калеб

10

Якщо ви використовуєте Celery + Django, найпростіший спосіб перевірити завдання, використовуючи команди безпосередньо з вашого терміналу у вашому віртуальному середовищі або використовуючи повний шлях до селери:

Документ : 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

Також якщо ви використовуєте Celery + RabbitMQ, ви можете ознайомитись зі списком черг за допомогою наступної команди:

Більше інформації : https://linux.die.net/man/1/rabbitmqctl

$ sudo rabbitmqctl list_queues

4
Якщо у вас є визначений проект, ви можете використовуватиcelery -A my_proj inspect reserved
— sashaboulouds

6

Рішення для копіювання та вставки для Redis з серіалізацією 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

Це працює з Джанго. Просто не забудьте змінити yourproject.celery.


1
Якщо ви використовуєте серіализатор соління, то можете змінити body =рядок на body = pickle.loads(base64.b64decode(j['body'])).
— Джим Ханзікер

4

Можливо, модуль огляду селери знає про завдання лише з точки зору працівників. Якщо ви хочете переглянути повідомлення, які перебувають у черзі (ще їх не витягнуть робітники), я пропоную використовувати pyrabbit , який може взаємодіяти з rabbitmq http api для отримання всіх видів інформації з черги.

Приклад можна знайти тут: Отримайте довжину черги за допомогою селери (RabbitMQ, Django)


3

Я думаю, що єдиний спосіб отримати завдання, які чекають, - це зберегти список завдань, які ви розпочали, і дозволити завдання видалити себе зі списку при його запуску.

За допомогою rabbitmqctl та list_queues ви можете отримати огляд того, скільки завдань чекають, але не самі завдання: http://www.rabbitmq.com/man/rabbitmqctl.1.man.html

Якщо те, що ви хочете, включає завдання, яке обробляється, але воно ще не закінчене, ви можете зберегти список завдань і перевірити їх стан:

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

result.ready() # True if finished

Або ви дозволяєте Celery зберігати результати разом із CELERY_RESULT_BACKEND і перевіряти, які з ваших завдань там немає.


3

Це працювало для мене в моїй заявці:

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 буде список рядків, які відповідають завданням у черзі.

Не забудьте поміняти CELERY_APP_INSTANCE своїми власними.

Дякую @ashish за те, що він вказав мене в правильному напрямку зі своєю відповіддю тут: https://stackoverflow.com/a/19465670/9843399


в моєму випадку jobsзавжди нуль ... будь-яка ідея?
— daveoncode

@daveoncode Я не думаю, що це достатньо інформації для мене, щоб відповісти корисно. Ви можете відкрити власне запитання. Я не думаю, що це буде дублікатом цього, якщо ви вкажете, що ви хочете отримати інформацію в python. Я б повернувся до stackoverflow.com/a/19465670/9843399 , на чому я спирався на свою відповідь і переконуюсь, що це працює спочатку.
— Калеб шприц

@CalebSyring Це перший підхід, який дійсно показує мені завдання із черги. Дуже хороша. Єдина проблема для мене полягає в тому, що додавання до списку, здається, не працює. Будь-які ідеї, як я можу змусити функцію зворотного дзвінка записати до списку?
— Варлор

@Varlor Вибачте, хтось зробив неправильну редакцію моєї відповіді. Ви можете шукати в історії редагування оригінальну відповідь, яка, швидше за все, допоможе вам. Я працюю над тим, щоб це виправити. (EDIT: Я щойно зайшов і відхилив редагування, в якому була явна помилка python. Повідомте мене, чи вирішила це ваша проблема чи ні.)
— Caleb Syring

@CalebSyring Я тепер використовував ваш код у класі, маючи список як атрибут класу!
— Варлор

2

Наскільки мені відомо, селера не дає API для вивчення завдань, які чекають у черзі. Це специфічно для брокера. Якщо ви використовуєте Redis в якості брокера для прикладу, розгляд завдань, які чекають у celeryчерзі (за замовчуванням), такий же простий, як:

  1. підключитися до бази даних брокера
  2. елементи списку у celeryсписку (команда LRANGE для прикладу)

Майте на увазі, що це ЗАЧЕКУВАННЯ, які повинні обрати наявні працівники. У вашому кластері можуть бути запущені деякі завдання - їх не буде в цьому списку, як вони вже були вибрані.


1

Я прийшов до висновку, що найкращий спосіб отримати кількість завдань на черзі - це використовувати, rabbitmqctlяк було запропоновано тут кілька разів. Щоб дозволити будь-якому обраному користувачеві виконувати команду, sudoя дотримувався інструкцій тут (я пропустив редагування частини профілю, оскільки не проти вводити sudo перед командою.)

Я також схопив jamesc's grepі cutфрагмент і загорнув його в підпроцесові дзвінки.

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

Якщо ви керуєте кодом завдань, тоді ви зможете вирішити проблему, дозволивши задачі запустити тривіальну спробу при першому її виконанні, а потім перевіривши inspect().reserved(). Повторне завдання реєструє завдання з результатом пошуку, і селера може це бачити. Завдання має приймати selfабо contextяк перший параметр, щоб ми могли отримати доступ до кількості повторних спроб.

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

Це рішення є брокерським агностиком, тобто. вам не потрібно турбуватися про те, чи використовуєте Ви RabbitMQ або Redis для зберігання завдань.

EDIT: після тестування я вважав, що це лише часткове рішення. Розмір зарезервованого обмежений налаштуванням попереднього вибору для працівника.


0

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

Будьте обережні , щоб змінити my_projзyour_proj

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.