Celery: No se permite que las tareas inicien subprocesos

Creado en 29 nov. 2013  ·  68Comentarios  ·  Fuente: celery/celery

A partir de Celery 3.1.0, el grupo de procesos ( celery.concurrency.prefork , antes celery.concurrency.processes ) usa procesos demonio para realizar tareas.

Los procesos daemon no pueden crear procesos secundarios y, como resultado, las tareas que usan el paquete multiprocessing no funcionan:

[2013-11-29 14:27:48,297: ERROR/MainProcess] Task app.add[e5d184c0-471f-4fc4-804c-f760178d4847] raised exception: AssertionError('daemonic processes are not allowed to have children',)
Traceback (most recent call last):
  File "/Users/aromanovich/Envs/celery3.1/lib/python2.7/site-packages/celery/app/trace.py", line 218, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/Users/aromanovich/Envs/celery3.1/lib/python2.7/site-packages/celery/app/trace.py", line 398, in __protected_call__
    return self.run(*args, **kwargs)
  File "/Users/aromanovich/Projects/celery/app.py", line 10, in add
    manager = multiprocessing.Manager()
  File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/__init__.py", line 99, in Manager
    m.start()
  File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/managers.py", line 524, in start
    self._process.start()
  File "/usr/local/Cellar/python/2.7.6/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/process.py", line 124, in start
    'daemonic processes are not allowed to have children'
Not a Bug

Comentario más útil

@thedrow
Lo malinterpretaste. Dos veces.
Nuestra preocupación no es que no tengas el recurso (esto es perfectamente comprensible y, lamentablemente, es un caso muy común en el software libre). Nuestra preocupación es que el ticket esté cerrado por eso, no es así como funcionan los tickets.
No somos 'infelices', estamos conmocionados.

Todos 68 comentarios

Esto no ha cambiado entre 3.0 y 3.1, por lo que no estoy seguro de por qué obtendría este error ahora y no antes.

Así es como se puede reproducir este error.

app.py:

import multiprocessing
from celery import Celery

app = Celery(__name__, broker='amqp://192.168.33.40')
@app.task
def f():
    manager = multiprocessing.Manager()

sendtask.py:

import app

app.f.delay()

Ejecuto worker usando el siguiente comando: celery worker -A app.app -l debug .

Con Celery 3.0.24, la tarea tiene éxito:

[2013-12-02 20:43:56,454: INFO/MainProcess] Task app.f[bcaab028-dbec-43a8-9259-ff7c35ff13d0] 
succeeded in 0.0169339179993s: None

Con Apio 3.1.5 no:

[2013-12-02 20:48:38,946: ERROR/MainProcess] Task app.f[c9f1cdd3-ae38-493e-b7c7-b9636ed473d0] 
raised exception: AssertionError('daemonic processes are not allowed to have children',)

Mi comprensión del problema es la siguiente: celery.concurrency.prefork.TaskPool usa celery.concurrency.asynpool.AsynPool ; AsynPool hereda de billiard.pool.Pool que genera procesos de trabajo demonio y AsynPool no anula este comportamiento. Pero tienes razón, este esquema no parece haber cambiado entre 3.0 y 3.1, así que yo también estoy confundido :)

Y parece que no estoy solo con ese problema: http://stackoverflow.com/questions/20149421/threads-in-celery-3-1-5

Una diferencia es que el proceso de trabajo ahora es una subclase de 'Proceso', donde antes usaba el argumento de la función: Process(target=) , tal vez haya una diferencia en los valores predeterminados para estos enfoques.

multiprocesamiento y versiones antiguas de juegos de billar daemon=True :
https://github.com/celery/billiard/blob/2.7/billiard/pool.py#L904

Y es lo mismo en la última versión:
https://github.com/celery/billiard/blob/3.3/billiard/pool.py#L1039

Creo que el proceso de tareas al ser un demonio presenta una seria limitación para la implementación de tareas.
Escribí una tarea que usa multiprocesamiento para acelerar las operaciones vinculadas a la CPU. Todo funciona bien cuando inicio un trabajador en una terminal de la siguiente manera:

trabajador de apio --app = tasks -Q wb -l info --concurrency = 1

Pero cuando uso el script de celeryd para iniciar un trabajador, obtengo esta excepción:
AssertionError: los procesos demoníacos no pueden tener hijos

Descubrí qué causó el cambio en el comportamiento.
Las tareas se ejecutan usando procesos daemon tanto en 3.0 como en 3.1, pero hasta que el módulo celery / billiard @ 4c32d2e y https://github.com/celery/billiard/commit/c676b94aa4144349b11ab31c82296a5d804909c9 multiprocessing no lo sabía y, por lo tanto, estaba permitiendo crear subprocesos.

Según tengo entendido, hubo un error antes de la versión 3.1 (se permitió que las tareas crearan subprocesos, lo que podría resultar en un estado huérfano) y ahora este error se ha corregido.

La decisión de no permitir que los procesos del demonio de Python se bifurquen me parece bastante arbitraria. Si bien reconozco su buena fe, siento que debería poder tener un control total sobre este comportamiento si así lo deseo.

Estar vinculado a un proceso por tarea me parece una seria limitación. ¿Pensamientos?

Me pregunto por qué existe esa limitación en primer lugar, una advertencia que puedo entender, pero rechazarla por completo parece una tontería cuando eres perfectamente capaz de bifurcar procesos utilizando otros medios.

@preguntar , ¿sería posible inicializar el proceso de trabajador de apio con la bandera del demonio en Falso? ¿O hacer esto configurable?

@ilyastam parece que estábamos comentando al mismo tiempo

Estoy de acuerdo en que parece una limitación arbitraria, pero desearía saber la razón detrás de agregarlo en primer lugar.

Este es un error bien conocido en los sistemas posix, pero aún está permitido. Puede limpiar procesos secundarios en un manejador de señales, aunque eso no lo protege contra SIGKILL.

Creo que deberíamos eliminar la limitación del billar, aunque eso difiera del comportamiento de multiprocesamiento. Aún puede crear procesos secundarios usando el módulo subpocess o usando la llamada fork bajo nivel, por lo que los usuarios avanzados deberían poder crear instancias secundarias billiard.Process .

@ilyastam Debería poder eliminar la declaración de aumento, no es necesario que los procesos sean "no demonios"

Es decir, a los procesos demonio se les permitirá crear procesos secundarios incluso si no podrá cosecharlos,
que es como funciona posix de todos modos.

Por cierto, tenga en cuenta que esta no es una raise , es una declaración de aserción, que se eliminará si Python se inicia con el argumento PYTHONOPTIMIZE envvar o -O .

billar 3.3.0.11 está en PyPI incluido este cambio

@Preguntar gracias. ¿Alguna idea de qué versión de apio verá esta mejora?

multiprocessing establece explícitamente que los procesos daemon no pueden crear subprocesos y explica por qué. En cuanto a mí, esta declaración assert parece más bien que se colocó aquí como un atajo para raise (la gente suele hacer eso).

Esta limitación está documentada y no creo que sea una buena idea que Celery parchee silenciosamente multiprocessing y se lo quite. Podría tener consecuencias realmente inesperadas y dañinas.

Puedo pensar en el siguiente ejemplo (aunque puede parecer un poco artificial):

@app.task
def f():
    p = multiprocessing.Pool(3)
    p.map_async(time.sleep, [1000, 1000, 1000])

Al ejecutarse como una función simple de Python, este código funciona correctamente. Pero al ejecutarse como una tarea de Celery (usando Celery versión 3.0. *), Deja tres subprocesos que se colgarán para siempre; cuando el trabajador de Apio se retire, estos subprocesos quedarán huérfanos.

No explica por qué, solo indica el comportamiento de Unix que esperaría al iniciar un proceso niño-niño. Aunque es una limitación infame en Unix, no impide que la gente lo haga. Esto no es diferente de
iniciar un proceso subprocess.Popen , o incluso llamar a fork() para iniciar un nuevo proceso. Entonces, ¿por qué debería ser ilegal?

La forma de hacer tu ejemplo:

from billiard import Pool
from multiprocessing.util import Finalize

_finalizers = []

@app.task
def f():
    p = billiard.Pool(3)
    _finalizers.append(Finalize(p, p.terminate))
   try:
       p.map_async(time.sleep, [1000, 1000, 1000])
       p.close()
       p.join()
   finally:
       p.terminate()

Para matar (-9) esto, también tendría que matar -9 los procesos secundarios, pero eso es algo que tendrá
a tener en cuenta para todos los procesos de Unix.

No es que defienda la creación de un grupo para cada tarea, pero no veo por qué los usuarios, que saben lo que son
haciendo, no debería permitírsele iniciar procesos desde una tarea.

Además, no ponemos parches a nada, esto es solo un cambio en el billar.

Además, no ponemos parches a nada, esto es solo un cambio en el billar.

Por "parche de mono" me refiero a esta asignación, que reemplaza multiprocessing._current_process con una instancia de billiard.process.Process : https://github.com/celery/billiard/blob/master/billiard/process.py # L53.

Estoy de acuerdo en que no hay nada de malo en iniciar procesos niño-niño si se manejan correctamente (como en su ejemplo). Mi punto es que multiprocessing no está escrito de esa manera y no debemos ignorar sus limitaciones de _implementación_.

@aromanovich No se puede escribir de otra manera, no es una limitación del multiprocesamiento, es una limitación de Unix.

Establece _current_process para que la variable de formato de los módulos de registro processName funcione, y el objeto de proceso de billar tiene la misma API que el objeto de proceso de multiprocesamiento, por lo que es seguro establecer el proceso actual.

Y por cierto, tendría que usar el billar para que se levante la limitación, el uso del multiprocesamiento aún generará la excepción.

También podría solucionar este problema con este enfoque:
http://stackoverflow.com/questions/6974695/python-process-pool-non-daemonic
Lo que permitiría a los usuarios continuar usando el módulo de multiprocesamiento, evitando este problema:
https://github.com/celery/billiard/issues/99

Recibo este error cuando llamo a una tarea de tejido @parallel desde dentro de una tarea de apio.

@celery.task
def dostuff():
   execute(fabfile.push_settings, sid=site['sid'])

<strong i="7">@parallel</strong>
@roles(environment)
def push_settings(sid):
  #do stuff

@frodopwns usa ENV
exportar PYTHONOPTIMIZE = 1
para eliminar esta afirmación. necesitas manejar todas las cosas.

@xiaods creo que resolví ese problema con algo como esto:

@worker_process_init.connect
def configure_workers(sender=None, conf=None, **kwargs):
    Crypto.Random.atfork()

Problema

Tengo una tarea que calcula algunos datos y carga un clasificador scikit-learn para hacer predicciones basadas en esos datos. Cuando ejecuto la tarea por sí sola, todo está bien, pero cuando la ejecuto usando Celery, aparece un error cuando la tarea intenta cargar el clasificador en escabeche:

[2015-07-17 21:23:51,299: ERROR/MainProcess] Task app.f[329d0da4-2e0e-4e1f-8148-d64f47750b1f] raised unexpected: AttributeError("'Worker' object has no attribute '_config'",)
Traceback (most recent call last):
  File "/home/username/anaconda3/lib/python3.4/site-packages/celery/app/trace.py", line 240, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/home/username/anaconda3/lib/python3.4/site-packages/celery/app/trace.py", line 438, in __protected_call__
    return self.run(*args, **kwargs)
  File "/home/username/working/playground/celery/app.py", line 11, in f
    clf = pickle.load(open('clf.pickle', 'rb'))
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/ensemble/__init__.py", line 6, in <module>
    from .base import BaseEnsemble
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/ensemble/base.py", line 13, in <module>
    from ..externals.joblib import cpu_count
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/externals/joblib/__init__.py", line 112, in <module>
    from .parallel import Parallel
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/externals/joblib/parallel.py", line 23, in <module>
    from ._multiprocessing_helpers import mp
  File "/home/username/anaconda3/lib/python3.4/site-packages/sklearn/externals/joblib/_multiprocessing_helpers.py", line 25, in <module>
    _sem = mp.Semaphore()
  File "/home/username/anaconda3/lib/python3.4/multiprocessing/context.py", line 81, in Semaphore
    return Semaphore(value, ctx=self.get_context())
  File "/home/username/anaconda3/lib/python3.4/multiprocessing/synchronize.py", line 127, in __init__
    SemLock.__init__(self, SEMAPHORE, value, SEM_VALUE_MAX, ctx=ctx)
  File "/home/username/anaconda3/lib/python3.4/multiprocessing/synchronize.py", line 59, in __init__
    kind, value, maxvalue, self._make_name(),
  File "/home/username/anaconda3/lib/python3.4/multiprocessing/synchronize.py", line 117, in _make_name
    return '%s-%s' % (process.current_process()._config['semprefix'],
AttributeError: 'Worker' object has no attribute '_config'

Reproducir

Cree un clasificador vacío y guárdelo como un pepinillo:

import pickle
from sklearn.ensemble import GradientBoostingClassifier
clf = GradientBoostingClassifier()
pickle.dump(clf, open('clf.pickle', 'wb'))

Cree una aplicación simple ( app.py ):

import pickle
import sklearn
from celery import Celery

app = Celery(__name__, broker='amqp://localhost//')

@app.task
def f():
    print('hello')
    clf = pickle.load(open('clf.pickle', 'rb'))
    print(clf)

Inicie el trabajador de apio:

celery -A app worker --loglevel=debug

Ejecute la aplicación:

python -c "from app import f; f.delay()"

Mensaje de error:

...
AttributeError: 'Worker' object has no attribute '_config'

Solución

Creo que debería haber una opción para "monkeypatch" Apio para permitir que las tareas inicien subprocesos, especialmente si tal "característica" existía en el pasado. En este momento, las personas simplemente se están mudando a otros marcos cuando se encuentran con este problema: http://stackoverflow.com/questions/27904162/using-multiprocessing-pool-from-celery-task-raises-exception. Aquí hay otro ejemplo de este error: http://stackoverflow.com/questions/22674950/python-multiprocessing-job-to-celery-task-but-attributeerror.

Este problema debería reabrirse ...

Me encontré con el mismo problema. Estoy usando nltk dentro de uno de mis trabajadores, que a su vez importa scikit-learn que conduce al mismo error que mostró @ostrokach .

Parece que puedo solucionar esto con el siguiente código:

from celery.signals import worker_process_init

@worker_process_init.connect
def fix_multiprocessing(**kwargs):
    from multiprocessing import current_process
    try:
        current_process()._config
    except AttributeError:
        current_process()._config = {'semprefix': '/mp'}

Obviamente, esto es un truco muy crudo y no sé qué pasaría si realmente usara el multiprocesamiento (diablos, ni siquiera sé qué es semprefix ) pero es suficiente para ganar scikit-learn trabajar de nuevo.

Dejo esto aquí para otras personas que tropiecen con el mismo problema hasta que se solucione este problema.

¿Podría ser esto algún tipo de incompatibilidad con el billar en Python 3? ¿O también es reproducible en Python 2?

¿Sigue siendo un problema el problema original de que los procesos de apio no puedan crear subprocesos? Mirando hacia atrás a través de los comentarios, se corrigió con celery / billiard @ e6bb0f7 para la versión 3.3. Sin embargo, una confirmación posterior (celery / billiard @ c7eedbd0ee1498e76d4fa1affac5b1a275660ee7) reintrodujo una declaración de aserción muy similar en el método de inicio para 3.4. No hay problema para mí ya que estoy en 3.3, pero solo quería señalar si podría crear problemas en el futuro.

@martinth ¡ Gracias, este truco también funciona para mí!

@xiaods ¡ Gracias! ¡Tu solución funciona para mí! ¡Gracias!

@gilinson sigue siendo un problema y exportar PYTHONOPTIMIZE = 1 todavía lo "arregla un poco".
Me encontré con el mismo problema al intentar ejecutar el libro de jugadas ansible en la tarea de apio

@martinth ¡ Gracias por el truco! Me encuentro con el mismo problema con:

  • Python 3.4.3
  • apio == 3.1.18
  • scikit-learn == 0.17

El truco de @martinth no funciona para mí, me encontré con esto tratando de usar el multiprocesamiento para acelerar algunos cálculos. En su lugar, usé el módulo de subprocesos y parece aliviar este error mientras aún interrumpe mi procesamiento.

El uso del multiprocesamiento basado en subprocesos.dummy funcionó en apio para mí:

from multiprocessing.dummy import Pool

Este error también ocurre en Python 2.7.5. No estoy seguro de si tiene la intención de abordarlo alguna vez, pero esto hace que el uso de salt-ssh de saltstack no se pueda usar con apio.

Cerrando esto, ya que no tenemos los recursos para completar esta tarea.

Solución posible"

Tenía una tarea de este tipo que intentaba crear hilos y esto fallaría. Logré que funcionara: bifurcando a un script bash que a su vez se bifurca a un intérprete de Python que hace exactamente el mismo código (y por lo tanto podría crear subprocesos, lo cual era crítico para mi caso de uso).

No entiendo por qué el ticket está cerrado. Si no tiene recursos para eso, puede hacer un comentario sobre esto, pero esto no cierra el ticket. Solo estás ocultando el error al hacerlo.

Lo que es especialmente malo para un ticket etiquetado como "crítico" tanto por su prioridad como por su gravedad.

@orzel +1.
Prioridad: crítica
Gravedad: crítica
Cerrando esto, ya que no tenemos los recursos para completar esta tarea.

Eso es una broma. Si no tiene recursos ahora, no lo arregle ahora. Arréglalo cuando tengas recursos. Cerrar el ticket no eliminará el problema

@orzel @Templarrr Etiqueté este boleto como Crítico, por lo que @ask no tiene la culpa aquí.
Puede que no estés contento con esto, pero protestar no ayudará.
Necesitamos preparar nuestro backlog basándonos en lo que es procesable y lo que no lo es y actualmente no lo es.
Es una decisión difícil de tomar, pero alguien tiene que hacerlo.
Si este problema se interpone en su camino, intente solucionarlo. Prometo que si la solución es correcta y tiene las pruebas adecuadas, la fusionaré.

@thedrow
Lo malinterpretaste. Dos veces.
Nuestra preocupación no es que no tengas el recurso (esto es perfectamente comprensible y, lamentablemente, es un caso muy común en el software libre). Nuestra preocupación es que el ticket esté cerrado por eso, no es así como funcionan los tickets.
No somos 'infelices', estamos conmocionados.

También estoy completamente en desacuerdo con cerrar esto.

Creo que todos podemos estar de acuerdo en que esto es un error. Y aunque es realmente triste que no haya suficientes recursos, cerrar un error _definite_ no ayudará con esto. No es posible saber si tal vez mañana alguien venga y piense "arreglemos algunos errores en Apio" solo para ver los problemas abiertos y piense "Bueno, no hay ningún trabajo interesante que hacer aquí ... trabajemos en _OtherProject_ en su lugar ".
Además, cerrar este problema hará que sea más difícil encontrarlo. No sé cómo usa Github, pero cuando descubro un problema potencial, primero busco en el rastreador de problemas el problema abierto. Por lo general, hay mucha discusión y más, nunca hay una solución alternativa (como en este caso) que puedo usar por ahora. Solo si me desespero de verdad, empiezo a revisar los problemas cerrados.

Esto no es un "arreglo de trabajos pendientes", es un ajuste de números. Si miro cosas para usar, _hago_ miro el recuento de problemas abiertos, pero también miro siempre el recuento de estrellas (que es bastante alto para el apio). Entiendo que es deseable tener un recuento bajo de errores para el atractivo público, pero también por su propio bien. Honestamente, entiendo que ver "250 problemas abiertos" no es un buen número y suena abrumador.

Si no tiene la mano de obra para trabajar en esto durante el próximo mes (o incluso el año), está bien. Simplemente no cierres. Un cierre solo debería ocurrir si el problema está terminado o si está _absolutamente claro_ que _nunca_ se hará. Ambos no son el caso aquí.

Simplemente elimine los indicadores "Crítico" y agregue un indicador "Aplazado" para cualquier cosa que no se pueda manejar ahora pero que se deba manejar si el recurso _está_ disponible.

No estoy seguro de que podamos solucionar el problema. ¿No podemos cambiar el funcionamiento de Unix, pero podríamos enviar un parche para eliminar la restricción?

Tal vez haya soluciones específicas de plataforma en Linux, pero eso tendría que investigarse. Ha estado abierto durante 2 años sin que nadie tenga el incentivo de arreglarlo, por lo que es poco probable que se solucione en la función cercana.

Cerré más de 200 ediciones y marqué más de 30k correos electrónicos como leídos, por lo que algunos de ellos seguramente serán polémicos y es posible que tengamos que volver a abrirlos. Lo espero completamente, pero sería bueno si también pudiéramos contribuir a una solución, por ejemplo, ayudando a documentar el defecto si esa es la única opción conocida.

Estamos abrumados por el trabajo, tratando de operar un gran proyecto sin recursos. No podemos clasificar los problemas ni averiguar qué problemas ya se han solucionado.

Bueno esta bien. Pero, ¿podría al menos estar documentado el hecho de que "No se puede utilizar el multiprocesamiento si se escribe código para un trabajador del apio"? Quiero decir ... siempre habrá gente que no lo lea, pero al menos puedes señalarlo y decir "Mira, está documentado. No podemos cambiarlo. Enfréntalo".

Mi lista de tareas es gigantesca, puede editar la documentación directamente en github ahora, por lo que es realmente fácil contribuir con cambios como estos :(

No hago esto para ocultar problemas, lo hago para impulsar a la gente a la acción precisamente porque quiero que mejore.

@ask ¿Podemos aplicar multiprocesamiento dentro de la tarea usando apio en django?
¿Existe alguna alternativa para hacerlo?

@abhisheksachan deberías leer todo este número antes de publicar una pregunta de este tipo.

@abhisheksachan No he probado esto en un par de años, pero lo había hecho funcionar usando https://pypi.python.org/pypi/billiard porque permite la demonización de subprocesos.

Sí, debe reemplazar las importaciones de 'multiprocesamiento' con 'billar', por ejemplo:

from multiprocessing import Process

->

from billiard import Process

No hay forma de que deshabilitemos la limitación de multiprocesamiento, pero argumentamos que no debería haber una limitación de todos modos, por lo que nuestra bifurcación de multiprocesamiento lo permite.

Para cualquiera, que como yo, invirtió en el desarrollo de un sistema de cola _ ANTES_ de descubrir esa limitación y necesita una solución alternativa hasta que puedan migrar a una envoltura de python rabbitMQ más utilizable, logré solucionar el problema llamando a un subproceso externo que puede horquilla limpiamente. Ese proceso bifurcado ahora está fuera de la caja de arena de apio y las cosas funcionan como deberían.

En el ejemplo de OP, reemplace:

app = Celery(__name__, broker='amqp://192.168.33.40') 
@app.task
def f():
    manager = multiprocessing.Manager()

con:

app = Celery(__name__, broker='amqp://192.168.33.40')
@app.task
def f():
    process = subprocess.Popen(["program"]) # or the newer post 3.5 run version
    process.wait()
    # analyze exit code

y el "programa" se verá así (en la plataforma POSIX unix / linux)

import os

def main():
      manager = multiprocessing.Manager()

# this is equivalent to "(cmd )&" under bash
pid = os.fork()
if pid == 0:
    cpid = os.fork()
    if cpid == 0:
        main()
    else:
        exit(0)
else:
    os.wait(pid)

Tenga en cuenta que la administración de la CPU escapa al alcance del apio, lo que va en contra de la idea de usar apio, pero dado que iba a usar el multiprocesamiento, probablemente desee manejar el uso de la CPU fuera del apio de todos modos.

Como mínimo, esa limitación debería estar documentada. Miré a mi alrededor en el documento y no pude encontrar.

Nuevamente, no dude en enviar una solicitud de extracción con los cambios en la documentación.

siguiendo el comentario de @martinth , en Python 3.5.2, Celery 4.0.0 y billiard 3.5.0, su solución no funcionó, ya que el multiprocesamiento verifica el proceso que se está demonizando y evita que inicie un niño.

Pude levantar la restricción restableciendo la bandera del demonio del trabajador. Estoy bastante seguro de que es una mala idea, pero permite iniciar el multiprocesamiento de piscinas desde dentro de una tarea de apio.

@worker_process_init.connect
def fix_multiprocessing(**kwargs):
    # don't be a daemon, so we can create new subprocesses
    from multiprocessing import current_process
    current_process().daemon = False

Dicho esto, en mi humilde opinión, Celery debería agregar una opción documentada para configurar si inicia a los trabajadores como demonios. Tenga en cuenta que estoy usando apio en una vaina k8 para que el apio se inicie como un proceso en primer plano usando celery worker , y realmente no necesito trabajadores demonizados.

@miraculixx El problema con esta sugerencia es que tendremos más modos de falla que manejar y más problemas que abordar. Preferimos evitarlos.

Aunque el uso de multiprocesamiento falla en combinación con el grupo prefork, parece funcionar cuando se usa el grupo en solitario. Entonces, supongo que una solución alternativa sería generar varios trabajadores de apio con el grupo individual, en lugar de uno con varios hijos en el grupo prefork. ¿Suena esto legítimo? Por supuesto, de esa forma algunas opciones como max-mem-per-child no funcionarán.

Creo que esto es básicamente un problema de diseño de aplicaciones. Es un dolor especial enfrentar daemonic processes are not allowed to have children , porque ya sabes, llegaste a un punto en el que tienes que rediseñar toda la aplicación. Pero es una limitación del nivel del sistema operativo, no se puede eludir sin efectos secundarios graves. Los procesos demoníacos tampoco pueden tener hijos en C. Esto no es algo específico de Python. Solía ​​haber un debate sobre el rendimiento del subproceso frente al proceso, y como conclusión resultó que ninguno de ellos es significativamente mejor o peor que el otro.

Sugiero dos opciones (y hablando en general, no de apio aquí)

  • Use subprocess.Popen para generar un proceso independiente, que puede tener hijos y usar sockets UNIX para la comunicación entre procesos
  • ¿Es realmente necesario que los hilos se generen mediante un proceso bifurcado y no su proceso principal?

Por lo que vale, mi caso de uso en ese entonces era que quería lanzar un subproceso de ejecución prolongada que a menudo se bloqueaba rápidamente debido a problemas de entrada no triviales (y no sensibles a la seguridad). Así que la idea era al menos asegurarse de que el proceso se iniciara con éxito.

A largo plazo, resultó ser un diseño deficiente por varias razones, por lo que la nueva arquitectura naturalmente volvió al uso "natural" de los trabajadores de apio asíncronos. Entonces estoy de acuerdo con la idea de cuestionar si la bifurcación es realmente necesaria; la tarea _es_ la bifurcación.

por lo que vale, mi caso de uso fue lanzar procesos scikit-learn que usan multiprocesamiento (a través de joblib). Desde entonces, he desarrollado un backend de apio para joblib, lo que significa que scikit-learn inicia procesos paralelos utilizando apio y mi truco anterior ya no es necesario. Esto está en una etapa de POC, aún no está listo para el horario de máxima audiencia.

@miraculixx ¿Tiene esto alojado en algún lugar? Me gustaría echarle un vistazo y / o probarlo. Me estoy encontrando con el mismo problema que usted, subprocesos de generación de sklearn, y básicamente me he rendido con Apio.

@pgeez Si no le importa el uso de subprocesos en sklearn, puede establecer la variable de entorno JOBLIB_MULTIPROCESSING = 0. Consulte https://github.com/scikit-learn/scikit-learn/blob/0.18.X/sklearn/externals/joblib/_multiprocessing_helpers.py

@jennaliu gracias por el pensamiento, pero como @miraculixx , necesito multiprocesamiento habilitado.

¿Habéis probado el viejo truco del tenedor doble de Unix para repudiar a los niños del proceso del demonio del apio?

¿Leíste el título de este hilo?!?!?

@sebastroy obviamente, he estado siguiendo este hilo durante años. Acabo de descubrir la bifurcación doble , pero ahora veo que mi confusión era pensar que las bifurcaciones demoníacas estaban siendo matadas por el apio, no que se las estuviera previniendo directamente.

Entendido. Sí, en mi vida anterior estaba usando C, así que esto era como pan y mantequilla.

La solución alternativa que estoy usando es subprocess.Popen, que funciona bien, pero luego necesita volver a implementar algo de lógica (y hacer una versión de shell del programa) que es lo que apio debería estar haciendo en primer lugar. Pero lo arreglé cambiando el comportamiento de la implementación de la API de nivel superior. Creo que está más alineado con el propósito del apio. Simplifique algo de lógica también que el nivel bajo.

Afortunadamente, encuentro este problema cuando intento ejecutar el libro de jugadas ansible en la tarea de Apio.
El método proporcionado por @martinth no me ha funcionado. Imprimo el current_process()._config y obtengo
{'authkey': b"y&e\x8d'\xcb\xd4\r\xd2\x86\x06\xe7\x9e\x14\xaf \xbc\xc4\x95\xa5G\xec&[i\x19\xf3G-\x06\xac\x19", 'semprefix': '/mp', 'daemon': True} .
Luego reasigno el campo daemon a False , y funciona.

¿Hay alguna solución u otra implementación que permita ejecutar multiproceso en la tarea?

@HeartUnchange : recientemente estamos trabajando duro en un proyecto de big data, que deseamos utilizar el apio como componente distribuido. y con su guía, tenemos la suerte de resolver el problema. ver la configuración de la tarea:

     @app.task
    def handleBigZipfile(filename,nid):
    current_process()._config['daemon'] = False
    logger.info('{} begin handle!'.format(filename))
    handleAll(filename,nid)
     logger.info('{} is done!'.format(filename))

¡La solución está bien! ¡Comenzamos el proyecto en 2017.1 y ahora el prototipo está terminado! ¡Han pasado nueve meses! ¡Soy dueño de mi agradecimiento! y mi agradecimiento es indescriptible!
¿Podría describir más acerca de cómo resolver el problema? ¡Estamos ansiosos por saberlo!

Hola ,

Tengo una configuración bastante estándar: Django + Rabbitmq + celery-4.0.2 + python-2.7 + centOS-7

Estoy tratando de generar un proceso utilizando el módulo de multiprocesamiento estándar de Python en apio.

Los procesos daemon no pueden crear procesos secundarios y, como resultado, las tareas que usan paquetes de multiprocesamiento no funcionan:
Comando utilizado para ejecutar: celery worker -B -A celery_task -l debug
Registros de seguimiento:

[2017-09-26 23:27:08,838: WARNING/PoolWorker-2] ERROR
[2017-09-26 23:27:08,839: WARNING/PoolWorker-2] Traceback (most recent call last):
[2017-09-26 23:27:08,839: WARNING/PoolWorker-2] File "/home/induser/config.py", line 612, in main
[2017-09-26 23:27:08,840: WARNING/PoolWorker-2] mylog_process = mp.Process(target=test_logger_process, args=(myqueue,))
[2017-09-26 23:27:08,840: WARNING/PoolWorker-2] File "/usr/lib64/python2.7/multiprocessing/process.py", line 98, in __init__
[2017-09-26 23:27:08,841: WARNING/PoolWorker-2] self._authkey = _current_process._authkey
[2017-09-26 23:27:08,841: WARNING/PoolWorker-2] AttributeError: 'Process' object has no attribute '_authkey'

¿Cuál podría ser la razón para no generar el proceso?
Aquí está el código:

import multiprocessing as mp
from celery.schedules import crontab
from celery.decorators import periodic_task

@periodic_task(run_every=crontab(minute='*/1'), name='test_process_celery')
def main():
data = config_read()
try:
    myqueue = mp.Queue(-1)
    mylog_process = mp.Process(target=test_logger_process, args=(myqueue,))
    mylog_process.start()
    . . .
    . . .
except Exception as e:
    raise
finally:
    mylog_process.join()

Gracias.

intente master e informe si sigue siendo el problema

Todavía tiene el error. Intenté usar un subproceso con:

from multiprocessing import Process, Value
import ctypes

[...]
        result = Value('i', 0)
        text = Value(ctypes.c_char_p, fail_string.encode())
        p = Process(target=reader.find_text_async, args=(result, text, ))
        p.start()
        p.join(5)

        if p.is_alive():
            logging.WARNING("Manual terminating the process 'find_text_async'")
            p.terminate()

pero con la rama maestra de apio dice:

Archivo "/usr/lib/python3.5/multiprocessing/process.py", línea 103, en inicio
'los procesos demoníacos no pueden tener hijos'
AssertionError: los procesos demoníacos no pueden tener hijos

EDITAR

Cambié el multiprocesamiento con billar y ¡funciona!

from billiard import Process, Value

¿Fue útil esta página
0 / 5 - 0 calificaciones