Can't seem to make python celery signals work

I'm rather new to celery development and I have an issue implementing signals. I have an application that consists of many different workers. currently it uses rabbitmq as a broker and redis as a backend.

Each worker has its own queue. This is the way we have it configured at the moment :

celery = Celery(queueDict['test'], broker=config.REDIS_SERVER, backend=config.REDIS_SERVER) default_exchange = Exchange('default', type='direct') test_queue = Queue(queueDict['test'], default_exchange, routing_key=queueDict['test']) logger = get_task_logger(__name__) celery.conf.task_queues = (test_queue, ) @celery.task(name='signal2', bind=True) def signal2(self, param): print("dog" + param) 

I would like to use signals so that I will be able to catch failed tasks on any worker in the application. When I use it inside the same worker with a task_failure event it works. But I would like to have another worker catch these events (or even my flask app) but I seem to be missing something... Here is my current attempt at making it work.

celery = Celery('consumer', broker=config.REDIS_SERVER, backend=config.REDIS_SERVER) default_exchange = Exchange('default', type='direct') default_queue = Queue(queueDict['default'], default_exchange, routing_key=queueDict['default']) logger = get_task_logger(__name__) celery.conf.task_queues = (default_queue, ) @task_failure.connect def process_failure_signal(sender=None, task_id=None, exception=None, args=None, kwargs=None, traceback=None, einfo=None, **akwargs): msg = 'Signal exception: %s (%s)' % ( exception.__class__.__name__, exception) exc_info = (type(exception), exception, traceback) extra = { 'data': { 'task_id': str(task_id), 'sender': str(sender), 'args': str(args), 'kwargs': str(kwargs), } } logger.error(msg, exc_info=exc_info, extra=extra) 

But it never receives any signals... Thanks for the help.

2

2 Answers

DejanLekic was correct and the page he shared had exactly what I wanted.

for those interested:

This can be easily used to capture events and monitor tasks.

Real-time processing

To process events in real-time you need the following

  • An event consumer (this is the Receiver)

  • A set of handlers called when events come in.

    You can have different handlers for each event type, or a catch-all handler can be used ('*')

  • State (optional)

    app.events.State is a convenient in-memory representation of tasks and workers in the cluster that’s updated as events come in.

    It encapsulates solutions for many common things, like checking if a worker is still alive (by verifying heartbeats), merging event fields together as events come in, making sure time-stamps are in sync, and so on.

Combining these you can easily process events in real-time:

from celery import Celery def my_monitor(app): state = app.events.State() def announce_failed_tasks(event): state.event(event) # task name is sent only with -received event, and state # will keep track of this for us. task = state.tasks.get(event['uuid']) print('TASK FAILED: %s[%s] %s' % ( task.name, task.uuid, task.info(),)) with app.connection() as connection: recv = app.events.Receiver(connection, handlers={ 'task-failed': announce_failed_tasks, '*': state.event, }) recv.capture(limit=None, timeout=None, wakeup=True) if __name__ == '__main__': app = Celery(broker='amqp://guest@localhost//') my_monitor(app) 

Note: The wakeup argument to capture sends a signal to all workers to force them to send a heartbeat. This way you can immediately see workers when the monitor starts.

You can listen to specific events by specifying the handlers:

from celery import Celery def my_monitor(app): state = app.events.State() def announce_failed_tasks(event): state.event(event) # task name is sent only with -received event, and state # will keep track of this for us. task = state.tasks.get(event['uuid']) print('TASK FAILED: %s[%s] %s' % ( task.name, task.uuid, task.info(),)) with app.connection() as connection: recv = app.events.Receiver(connection, handlers={ 'task-failed': announce_failed_tasks, }) recv.capture(limit=None, timeout=None, wakeup=True) if __name__ == '__main__': app = Celery(broker='amqp://guest@localhost//') my_monitor(app) 

Monitoring and Management Guide — Celery 4.4.2 documention

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like