|
@@ -77,6 +77,12 @@ this is needed so that names can be automatically generated, the second
|
|
|
argument is the broker keyword argument which specifies the URL of the
|
|
|
message broker we want to use.
|
|
|
|
|
|
+The broker argument specifies the URL of the broker we want to use,
|
|
|
+we use RabbitMQ here, which is already the default option,
|
|
|
+but see :ref:`celerytut-broker` above if you want to use something different,
|
|
|
+e.g. for Redis you can use ``redis://localhost``, or MongoDB:
|
|
|
+``mongodb://localhost``.
|
|
|
+
|
|
|
We defined a single task, called ``add``, which returns the sum of two numbers.
|
|
|
|
|
|
.. _celerytut-running-celeryd:
|
|
@@ -115,7 +121,7 @@ Whenever we want to execute our task, we use the
|
|
|
|
|
|
This is a handy shortcut to the :meth:`~@Task.apply_async`
|
|
|
method which gives greater control of the task execution (see
|
|
|
-:ref:`guide-executing`).
|
|
|
+:ref:`guide-executing`)::
|
|
|
|
|
|
>>> from tasks import add
|
|
|
>>> add.delay(4, 4)
|
|
@@ -136,44 +142,55 @@ Keeping Results
|
|
|
|
|
|
If you want to keep track of the tasks state, Celery needs to store or send
|
|
|
the states somewhere. There are several
|
|
|
-built-in backends to choose from: SQLAlchemy/Django ORM, Memcached, Redis,
|
|
|
-AMQP, MongoDB, Tokyo Tyrant and Redis -- or you can define your own.
|
|
|
+built-in result backends to choose from: `SQLAlchemy`_/`Django`_ ORM,
|
|
|
+`Memcached`_, `Redis`_, AMQP (`RabbitMQ`_), and `MongoDB`_ -- or you can define your own.
|
|
|
+
|
|
|
+.. _`Memcached`: http://memcached.org
|
|
|
+.. _`MongoDB`: http://www.mongodb.org
|
|
|
+.. _`SQLAlchemy`: http://www.sqlalchemy.org/
|
|
|
+.. _`Django`: http://djangoproject.com
|
|
|
|
|
|
For this example we will use the `amqp` result backend, which sends states
|
|
|
-as messages. The backend is configured via the :setting:`CELERY_RESULT_BACKEND`
|
|
|
-setting or using the ``backend`` argument to :class:`Celery`, in addition individual
|
|
|
-result backends may have additional required or optional settings
|
|
|
-to configure::
|
|
|
+as messages. The backend is specified via the ``backend`` argument to
|
|
|
+:class:`@Celery`, (or via the :setting:`CELERY_RESULT_BACKEND` setting if
|
|
|
+you choose to use a configuration module)::
|
|
|
+
|
|
|
+ celery = Celery("tasks", backend="amqp", broker="amqp://")
|
|
|
+
|
|
|
+or if you want to use Redis as the result backend, but still use RabbitMQ as
|
|
|
+the message broker (a popular combination)::
|
|
|
|
|
|
- celery = Celery(backend="amqp")
|
|
|
+ celery = Celery("tasks", backend="redis://localhost", broker="amqp://")
|
|
|
|
|
|
To read more about result backends please see :ref:`task-result-backends`.
|
|
|
|
|
|
Now with the result backend configured, let's execute the task again.
|
|
|
-This time we'll hold on to the :class:`~@AsyncResult`::
|
|
|
+This time we'll hold on to the :class:`~@AsyncResult` instance returned
|
|
|
+when you apply a task::
|
|
|
|
|
|
>>> result = add.delay(4, 4)
|
|
|
|
|
|
-Here's some examples of what you can do when you have results::
|
|
|
+Here's some examples of what you can do with the result instance::
|
|
|
|
|
|
- >>> result.ready() # returns True if the task has finished processing.
|
|
|
+ >>> result.ready() # returns True if the task has finished processing.
|
|
|
False
|
|
|
|
|
|
- >>> result.result # task is not ready, so no return value yet.
|
|
|
+ >>> result.result # task is not ready, so no return value yet.
|
|
|
None
|
|
|
|
|
|
- >>> result.get() # Waits until the task is done and returns the retval.
|
|
|
+ >>> result.get() # waits for the task and returns its retval.
|
|
|
8
|
|
|
|
|
|
- >>> result.result # direct access to result, doesn't re-raise errors.
|
|
|
+ >>> result.result # direct access to result, doesn't re-raise errors.
|
|
|
8
|
|
|
|
|
|
>>> result.successful() # returns True if the task didn't end in failure.
|
|
|
True
|
|
|
|
|
|
-If the task raises an exception, the return value of :meth:`~@AsyncResult.successful`
|
|
|
-will be :const:`False`, and `result.result` will contain the exception instance
|
|
|
-raised by the task.
|
|
|
+If the task raises an exception, the return value of
|
|
|
+:meth:`~@AsyncResult.failed` will be :const:`True`, and `result.result` will
|
|
|
+contain the exception instance raised by the task, and `result.traceback`
|
|
|
+will contain the original traceback as a string.
|
|
|
|
|
|
.. _celerytut-configuration:
|
|
|
|
|
@@ -190,20 +207,36 @@ are many things to tweak so that Celery works just the way you want it to.
|
|
|
Reading about the options available is a good idea to get familiar with what
|
|
|
can be configured, see the :ref:`configuration` reference.
|
|
|
|
|
|
-The configuration can be set on the app directly (but not all at runtime)
|
|
|
-or by using a dedicated configuration module.
|
|
|
-As an example you can set the default value for the workers
|
|
|
-``--concurrency`` argument, which is used to decide the number of pool worker
|
|
|
-processes, by changing the :setting:`CELERYD_CONCURRENCY` setting:
|
|
|
+The configuration can be set on the app directly or by using a dedicated
|
|
|
+configuration module.
|
|
|
+As an example you can configure the default serializer used for serializing
|
|
|
+task payloads by changing the :setting:`CELERY_TASK_SERIALIZER` setting:
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
- celery.conf.CELERY_CONCURRENCY = 10
|
|
|
+ celery.conf.CELERY_TASK_SERIALIZER = "json"
|
|
|
|
|
|
-If you are configuring many settings then one practice is to have a separate module
|
|
|
-containing the configuration. You can tell your Celery instance to use
|
|
|
-this module, historically called ``celeryconfig.py``, with the
|
|
|
-:meth:`config_from_obj` method:
|
|
|
+If you are configuring many settings at once you can use ``update``:
|
|
|
+
|
|
|
+.. code-block:: python
|
|
|
+
|
|
|
+ celery.conf.update(
|
|
|
+ CELERY_TASK_SERIALIZER="json",
|
|
|
+ CELERY_RESULT_SERIALIZER="json",
|
|
|
+ CELERY_TIMEZONE="Europe/Oslo",
|
|
|
+ CELERY_ENABLE_UTC=True,
|
|
|
+ )
|
|
|
+
|
|
|
+For larger projects using a dedicated configuration module is useful,
|
|
|
+in fact you are discouraged from hard coding
|
|
|
+periodic task intervals and task routing options, as it is much
|
|
|
+better to keep this in a centralized location, and especially for libaries
|
|
|
+it makes it possible for users to control how they want your tasks to behave,
|
|
|
+you can also imagine your sysadmin making simple changes to the configuration
|
|
|
+in the event of system trobule.
|
|
|
+
|
|
|
+You can tell your Celery instance to use a configuration module,
|
|
|
+often called ``celeryconfig.py``, with :meth:`config_from_obj` method:
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
@@ -212,9 +245,17 @@ this module, historically called ``celeryconfig.py``, with the
|
|
|
A module named ``celeryconfig.py`` must then be available to load from the
|
|
|
current directory or on the Python path, it could look like this:
|
|
|
|
|
|
-:file:`celeryconfig.py`::
|
|
|
+:file:`celeryconfig.py`:
|
|
|
+
|
|
|
+.. code-block:: python
|
|
|
+
|
|
|
+ BROKER_URL = "amqp://"
|
|
|
+ CELERY_RESULT_BACKEND = "amqp://"
|
|
|
|
|
|
- CELERY_CONCURRENCY = 10
|
|
|
+ CELERY_TASK_SERIALIZER = "json"
|
|
|
+ CELERY_RESULT_SERIALIZER = "json"
|
|
|
+ CELERY_TIMEZONE = "Europe/Oslo"
|
|
|
+ CELERY_ENABLE_UTC = True
|
|
|
|
|
|
To verify that your configuration file works properly, and does't
|
|
|
contain any syntax errors, you can try to import it::
|
|
@@ -223,6 +264,42 @@ contain any syntax errors, you can try to import it::
|
|
|
|
|
|
For a complete reference of configuration options, see :ref:`configuration`.
|
|
|
|
|
|
+To demonstrate the power of configuration files, this how you would
|
|
|
+route a misbehaving task to a dedicated queue:
|
|
|
+
|
|
|
+:file:`celeryconfig.py`:
|
|
|
+
|
|
|
+.. code-block:: python
|
|
|
+
|
|
|
+ CELERY_ROUTES = {
|
|
|
+ "tasks.add": "low-priority",
|
|
|
+ }
|
|
|
+
|
|
|
+Or instead of routing it you could rate limit the task
|
|
|
+instead, so that only 10 tasks of this type can execute in a minute
|
|
|
+(10/m):
|
|
|
+
|
|
|
+:file:`celeryconfig.py`:
|
|
|
+
|
|
|
+.. code-block:: python
|
|
|
+
|
|
|
+ CELERY_ANNOTATIONS = {
|
|
|
+ "tasks.add": {"rate_limit": "10/m"}
|
|
|
+ }
|
|
|
+
|
|
|
+But in fact, if you are using one of RabbitMQ, Redis or MongoDB as the
|
|
|
+broker then you can actually direct the workers to set new rate limit
|
|
|
+for the task at runtime::
|
|
|
+
|
|
|
+ $ python tasks.py rate_limit tasks.add 10/m
|
|
|
+ worker.example.com: OK
|
|
|
+ new rate limit set successfully
|
|
|
+
|
|
|
+See :ref:`guide-routing` to read more about task routing,
|
|
|
+and the :setting:`CELERY_ANNOTATIONS` setting for more about annotations,
|
|
|
+or :ref:`guide-monitoring` for more about remote control commands,
|
|
|
+and how to monitor what your workers are doing.
|
|
|
+
|
|
|
Where to go from here
|
|
|
=====================
|
|
|
|