J'utilise du céleri (pool solo avec concurrence = 1) et je veux pouvoir arrêter le travailleur après l'exécution d'une tâche particulière. Une mise en garde est que je veux éviter toute possibilité que le travailleur reprenne d'autres tâches après celle-ci.
Voici ma tentative dans le contour:
from __future__ import absolute_import, unicode_literals
from celery import Celery
from celery.exceptions import WorkerShutdown
from celery.signals import task_postrun
app = Celery()
app.config_from_object('celeryconfig')
@app.task
def add(x, y):
return x + y
@task_postrun.connect(sender=add)
def shutdown(*args, **kwargs):
raise WorkerShutdown()
Cependant, lorsque je lance le travailleur
celery -A celeryapp worker --concurrency=1 --pool=solo
et exécutez la tâche
add.delay(1,4)
J'obtiens ce qui suit:
-------------- celery@sam-APOLLO-2000 v4.0.2 (latentcall)
---- **** -----
--- * *** * -- Linux-4.4.0-116-generic-x86_64-with-Ubuntu-16.04-xenial 2018-03-18 14:08:37
-- * - **** ---
- ** ---------- [config]
- ** ---------- .> app: __main__:0x7f596896ce90
- ** ---------- .> transport: redis://localhost:6379/0
- ** ---------- .> results: redis://localhost/
- *** --- * --- .> concurrency: 4 (solo)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[2018-03-18 14:08:39,892: WARNING/MainProcess] Restoring 1 unacknowledged message(s)
La tâche est re-mise en file d'attente et sera exécutée à nouveau sur un autre travailleur, conduisant à une boucle.
Cela se produit également lorsque je déplace l'exception WorkerShutdown
dans la tâche elle-même.
@app.task
def add(x, y):
print(x + y)
raise WorkerShutdown()
Existe-t-il un moyen d'arrêter le travailleur après une tâche particulière, tout en évitant cet effet secondaire malheureux?
Le processus recommandé pour arrêter un travailleur consiste à envoyer le signal TERM
. Cela entraînera l'arrêt d'un travailleur de céleri après avoir terminé toutes les tâches en cours d'exécution. Si vous envoyez un signal QUIT
au processus principal du travailleur, celui-ci s'arrêtera immédiatement.
Les documents de céleri, cependant, discutent généralement de cela en termes de gestion du céleri à partir d'une ligne de commande ou via systemd/initd, mais céleri fournit en outre une API de contrôle de travailleur distant via celery.app.control
.
Vous pouvez révoquer une tâche pour empêcher les travailleurs d'exécuter la tâche. Cela devrait empêcher la boucle que vous rencontrez. En outre, le contrôle prend également en charge arrêt d'un travailleur.
J'imagine donc que ce qui suit vous donnera le comportement que vous désirez.
@app.task(bind=True)
def shutdown(self):
app.control.revoke(self.id) # prevent this task from being executed again
app.control.shutdown() # send shutdown signal to all workers
Puisqu'il n'est actuellement pas possible d'acquitter la tâche à partir de la tâche, puis de continuer à exécuter cette tâche, cette méthode d'utilisation de revoke
contourne ce problème de sorte que, même si la tâche est à nouveau mise en file d'attente, le nouveau travailleur ignorera simplement il.
Alternativement, les éléments suivants empêcheraient également l'exécution d'une tâche redistribuée une deuxième fois ...
@app.task(bind=True)
def some_task(self):
if self.request.delivery_info['redelivered']:
raise Ignore() # ignore if this task was redelivered
print('This should only execute on first receipt of task')
A noter également AsyncResult
possède également une méthode revoke
qui appelle self.app.control.revoke
pour vous.
Si vous arrêtez le programme de travail, une fois la tâche terminée, il ne sera plus mis en file d'attente.
@task_postrun.connect(sender=add)
def shutdown(*args, **kwargs):
app.control.broadcast('shutdown')
Cela arrêtera gracieusement le travailleur une fois les tâches terminées.
[2018-04-01 18:44:14,627: INFO/MainProcess] Connected to redis://localhost:6379/0
[2018-04-01 18:44:14,656: INFO/MainProcess] mingle: searching for neighbors
[2018-04-01 18:44:15,719: INFO/MainProcess] mingle: all alone
[2018-04-01 18:44:15,742: INFO/MainProcess] celery@foo ready.
[2018-04-01 18:46:28,572: INFO/MainProcess] Received task: celery_worker_stop.add[ac8a65ff-5aad-41a6-a2d6-a659d021fb9b]
[2018-04-01 18:46:28,585: INFO/ForkPoolWorker-4] Task celery_worker_stop.add[ac8a65ff-5aad-41a6-a2d6-a659d021fb9b] succeeded in 0.005628278013318777s: 3
[2018-04-01 18:46:28,665: WARNING/MainProcess] Got shutdown from remote
Remarque: la diffusion arrêtera tous les employés. Si vous souhaitez fermer un employé spécifique, démarrez-le avec un nom
celery -A celeryapp worker -n self_killing --concurrency=1 --pool=solo
Vous pouvez maintenant l'arrêter avec le paramètre de destination.
app.control.broadcast('shutdown', destination=['celery@self_killing'])
Si vous devez arrêter un travailleur spécifique et que vous ne connaissez pas son nom à l'avance, vous pouvez l'obtenir à partir des propriétés de la tâche. Sur la base des réponses ci-dessus, vous pouvez utiliser:
app.control.shutdown(destination=[self.request.hostname])
ou
app.control.broadcast('shutdown', destination=[self.request.hostname])
Remarque:
'-n'
);bind=True
paramètre.