Параллельная обработка в очереди (с использованием пула в Celery)

Я использую Celery для постановки в очередь заданий из CGI-приложения, которое я создал. Как я это настроил, Celery заставляет каждое задание выполняться по одному или по два за раз, устанавливая CELERYD_CONCURRENCY = 1 или = 2 (чтобы они не перегружали процессор и не тормозили из-за потребления памяти). Очередь отлично работает благодаря совету, который я получил на StackOverflow.

Каждое из этих заданий занимает довольно много времени (примерно 30 минут серийно), но имеет неприятную возможность распараллеливания. По этой причине я использовал Pool.map, чтобы разделить его и выполнять работу параллельно. Он отлично работал из командной строки, и я получил время работы около 5 минут, используя новый многоядерный чип.

К сожалению, есть некоторое ограничение, которое не позволяет демоническому процессу иметь подпроцессы, и когда я запускаю причудливый параллельный код в очереди CGI, я получаю эту ошибку:

AssertionError: демонические процессы не могут иметь потомков

Я заметил, что у других людей есть были похожие вопросы, но я не могу найти ответ, который не потребовал бы полного отказа от Pool.map и усложнения код нити.

Каков правильный выбор дизайна здесь? Я могу легко запускать свои последовательные задания, используя очередь Celery. Я также могу выполнять гораздо более быстрые параллельные задания без очереди. Как мне подойти к этому и возможно ли получить то, что я хочу (как очередь, так и распараллеливание для каждого задания)?

Пара идей, которые у меня были (некоторые довольно хакерские):

  • The job sent to the Celery queue simply calls the command line program. That program can use Pool as it pleases, and then saves the result figures & data to a file (just as it does now).
    Downside: I won't be able to check on the status of the job or see if it terminated successfully. Also, system calls from CGI may cause security issues.
  • Obviously, if the queue is very full of jobs, I can make use of the CPU resources (by setting CELERYD_CONCURRENCY = 6 or so); this will allow many people to be "at the front of the queue" at once.
    Downside: Each job will spend a lot of time at the front of the queue; if the queue isn't full, there will be no speedup. Also, many partially finished jobs will be stored in memory at the same time, using much more RAM.
  • Use Celery's @task to parallelize within sub-jobs. Then, instead of setting CELERYD_CONCURRENCY = 1, I would set it to 6 (or however many sub jobs I'd like to allow in memory at a time).
    Downside: First of all, I'm not sure whether this will successfully avoid the "task-within-task" problem. But also, the notion of queue position may be lost, and many partially finished jobs may end up in memory at once.
  • Perhaps there is a way to call Pool.map and specify that the threads are non-daemonic? Or perhaps there is something more lightweight I can use instead of Pool.map? This is similar to an approach taken on another open StackOverflow question. Also, I should note that the parallelization I exploit via Pool.map is similar to linear algebra, and there is no inter-process communication (each just runs independently and returns its result without talking to the others).
  • Throw away Celery and use multiprocessing.Queue. Then maybe there'd be some way to use the same "thread depth" for every thread I use (i.e. maybe all of the threads could use the same Pool, avoiding nesting)?

Заранее большое спасибо.


person user    schedule 07.10.2011    source источник


Ответы (2)


Что вам нужно, так это система управления рабочим процессом (WFMS), которая управляет

  • параллелизм задач
  • зависимость задачи
  • вложение задач

среди прочего.

С точки зрения очень высокого уровня, WFMS находится поверх пула задач, такого как сельдерей, и отправляет задачи, готовые к выполнению, в пул. Он также отвечает за открытие гнезда и соответствующую отправку задач в гнездо.

Я разработал систему именно для этого. Он называется pomsets. Попробуйте и не стесняйтесь присылать мне любые вопросы.

person michael pan    schedule 12.10.2011
comment
Это похоже на Sun Grid Engine? Я надеялся использовать что-то изнутри Python. Еще лучше, я бы хотел, чтобы кто-нибудь сказал, что правильно (на питоне) делать в этой ситуации в целом. - person user; 16.10.2011
comment
Я бы сказал, что SGE больше похож на сельдерей, поскольку они оба управляют очередью задач. Пока библиотеки доступны, помсеты могут отправляться в celery, SGE или любую другую очередь выполнения. А pomsets написан на Python, так что вы можете создавать рабочие процессы прямо в своей программе на Python. - person michael pan; 17.10.2011
comment
Лучшим решением было создать Celery задания и использовать их для запуска других Celery заданий. Но, в конечном счете, этот ответ является самым близким - по сути, я решил проблему, используя Celery в качестве единственной WFMS. - person user; 28.03.2012
comment
Я разместил исходный код на GitHub github.com/mjpan/pomsets-core. - person michael pan; 19.09.2016

Я использую многопроцессорные демоны на основе Twisted с разветвлением и обычным запросом заданий Gearman.

Попробуйте посмотреть на Gearman.

person Michael_XIII    schedule 16.10.2011
comment
Я не делал эту задачу, но думал, что они должны это сделать. Если попробуете - держите меня в курсе о результате. - person Michael_XIII; 17.10.2011