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


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.