extending.rst 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. .. _guide-extending:
  2. ==========================
  3. Extensions and Bootsteps
  4. ==========================
  5. .. contents::
  6. :local:
  7. :depth: 2
  8. .. _extending-bootsteps:
  9. Bootsteps
  10. =========
  11. Blahblah blah, example bootstep:
  12. .. code-block:: python
  13. from celery import Celery
  14. from celery import bootsteps
  15. class InfoStep(bootsteps.Step):
  16. def __init__(self, parent, **kwargs):
  17. # here we can prepare the Worker/Consumer object
  18. # in any way we want, set attribute defaults and so on.
  19. print('{0!r} is in init'.format(parent))
  20. def start(self, parent):
  21. # our step is started together with all other Worker/Consumer
  22. # bootsteps.
  23. print('{0!r} is starting'.format(parent))
  24. def stop(self, parent):
  25. # the Consumer calls stop every time the consumer is restarted
  26. # (i.e. connection is lost) and also at shutdown. The Worker
  27. # will call stop at shutdown only.
  28. print('{0!r} is stopping'.format(parent))
  29. def shutdown(self, parent):
  30. # shutdown is called by the Consumer at shutdown, it's not
  31. # called by Worker.
  32. print('{0!r} is shutting down'.format(parent))
  33. app = Celery(broker='amqp://')
  34. app.steps['worker'].add(InfoStep)
  35. app.steps['consumer'].add(InfoStep)
  36. Starting the worker with this step installed will give us the following
  37. logs::
  38. <celery.apps.worker.Worker object at 0x101ad8410> is in init
  39. <celery.worker.consumer.Consumer object at 0x101c2d790> is in init
  40. [2013-05-29 16:18:20,544: WARNING/MainProcess]
  41. <celery.apps.worker.Worker object at 0x101ad8410> is starting
  42. [2013-05-29 16:18:21,577: WARNING/MainProcess]
  43. <celery.worker.consumer.Consumer object at 0x101c2d8d0> is starting
  44. <celery.worker.consumer.Consumer object at 0x101c2d790> is stopping
  45. <celery.apps.worker.Worker object at 0x101ad8410> is stopping
  46. <celery.worker.consumer.Consumer object at 0x101c2d790> is shutting down
  47. The ``print`` statements will be redirected to the logging subsystem after
  48. the worker has been initialized, so the "is starting" lines are timestamped.
  49. You may notice that this does no longer happen at shutdown, this is because
  50. the ``stop`` and ``shutdown`` methods are called inside a *signal handler*,
  51. and it's not safe to use logging inside such a handler.
  52. Logging with the Python logging module is not :term:`reentrant`,
  53. which means that you cannot interrupt the function and
  54. call it again later. It's important that the ``stop`` and ``shutdown`` methods
  55. you write is also :term:`reentrant`.
  56. Starting the worker with ``--loglevel=debug`` will show us more
  57. information about the boot process::
  58. [2013-05-29 16:18:20,509: DEBUG/MainProcess] | Worker: Preparing bootsteps.
  59. [2013-05-29 16:18:20,511: DEBUG/MainProcess] | Worker: Building graph...
  60. <celery.apps.worker.Worker object at 0x101ad8410> is in init
  61. [2013-05-29 16:18:20,511: DEBUG/MainProcess] | Worker: New boot order:
  62. {Hub, Queues (intra), Pool, Autoreloader, Timer, StateDB,
  63. Autoscaler, InfoStep, Beat, Consumer}
  64. [2013-05-29 16:18:20,514: DEBUG/MainProcess] | Consumer: Preparing bootsteps.
  65. [2013-05-29 16:18:20,514: DEBUG/MainProcess] | Consumer: Building graph...
  66. <celery.worker.consumer.Consumer object at 0x101c2d8d0> is in init
  67. [2013-05-29 16:18:20,515: DEBUG/MainProcess] | Consumer: New boot order:
  68. {Connection, Mingle, Events, Gossip, InfoStep, Agent,
  69. Heart, Control, Tasks, event loop}
  70. [2013-05-29 16:18:20,522: DEBUG/MainProcess] | Worker: Starting Hub
  71. [2013-05-29 16:18:20,522: DEBUG/MainProcess] ^-- substep ok
  72. [2013-05-29 16:18:20,522: DEBUG/MainProcess] | Worker: Starting Pool
  73. [2013-05-29 16:18:20,542: DEBUG/MainProcess] ^-- substep ok
  74. [2013-05-29 16:18:20,543: DEBUG/MainProcess] | Worker: Starting InfoStep
  75. [2013-05-29 16:18:20,544: WARNING/MainProcess]
  76. <celery.apps.worker.Worker object at 0x101ad8410> is starting
  77. [2013-05-29 16:18:20,544: DEBUG/MainProcess] ^-- substep ok
  78. [2013-05-29 16:18:20,544: DEBUG/MainProcess] | Worker: Starting Consumer
  79. [2013-05-29 16:18:20,544: DEBUG/MainProcess] | Consumer: Starting Connection
  80. [2013-05-29 16:18:20,559: INFO/MainProcess] Connected to amqp://guest@127.0.0.1:5672//
  81. [2013-05-29 16:18:20,560: DEBUG/MainProcess] ^-- substep ok
  82. [2013-05-29 16:18:20,560: DEBUG/MainProcess] | Consumer: Starting Mingle
  83. [2013-05-29 16:18:20,560: INFO/MainProcess] mingle: searching for neighbors
  84. [2013-05-29 16:18:21,570: INFO/MainProcess] mingle: no one here
  85. [2013-05-29 16:18:21,570: DEBUG/MainProcess] ^-- substep ok
  86. [2013-05-29 16:18:21,571: DEBUG/MainProcess] | Consumer: Starting Events
  87. [2013-05-29 16:18:21,572: DEBUG/MainProcess] ^-- substep ok
  88. [2013-05-29 16:18:21,572: DEBUG/MainProcess] | Consumer: Starting Gossip
  89. [2013-05-29 16:18:21,577: DEBUG/MainProcess] ^-- substep ok
  90. [2013-05-29 16:18:21,577: DEBUG/MainProcess] | Consumer: Starting InfoStep
  91. [2013-05-29 16:18:21,577: WARNING/MainProcess]
  92. <celery.worker.consumer.Consumer object at 0x101c2d8d0> is starting
  93. [2013-05-29 16:18:21,578: DEBUG/MainProcess] ^-- substep ok
  94. [2013-05-29 16:18:21,578: DEBUG/MainProcess] | Consumer: Starting Heart
  95. [2013-05-29 16:18:21,579: DEBUG/MainProcess] ^-- substep ok
  96. [2013-05-29 16:18:21,579: DEBUG/MainProcess] | Consumer: Starting Control
  97. [2013-05-29 16:18:21,583: DEBUG/MainProcess] ^-- substep ok
  98. [2013-05-29 16:18:21,583: DEBUG/MainProcess] | Consumer: Starting Tasks
  99. [2013-05-29 16:18:21,606: DEBUG/MainProcess] basic.qos: prefetch_count->80
  100. [2013-05-29 16:18:21,606: DEBUG/MainProcess] ^-- substep ok
  101. [2013-05-29 16:18:21,606: DEBUG/MainProcess] | Consumer: Starting event loop
  102. [2013-05-29 16:18:21,608: WARNING/MainProcess] celery@example.com ready.
  103. .. figure:: ../images/worker_graph_full.png
  104. .. _extending-worker-bootsteps:
  105. Worker bootsteps
  106. ----------------
  107. Blablah
  108. .. _extending-consumer-bootsteps:
  109. Consumer bootsteps
  110. ------------------
  111. blahblah
  112. .. _extending-programs:
  113. Command-line programs
  114. =====================
  115. .. _extending-commandoptions:
  116. Adding new command-line options
  117. -------------------------------
  118. You can add additional command-line options to the ``worker``, ``beat`` and
  119. ``events`` commands by modifying the :attr:`~@Celery.user_options` attribute of the
  120. application instance.
  121. Celery commands uses the :mod:`optparse` module to parse command-line
  122. arguments, and so you have to use optparse specific option instances created
  123. using :func:`optparse.make_option`. Please see the :mod:`optparse`
  124. documentation to read about the fields supported.
  125. Example adding a custom option to the :program:`celery worker` command:
  126. .. code-block:: python
  127. from celery import Celery
  128. from optparse import make_option as Option
  129. celery = Celery(broker='amqp://')
  130. celery.user_options['worker'].add(
  131. Option('--enable-my-option', action='store_true', default=False,
  132. help='Enable custom option.'),
  133. )
  134. .. _extending-subcommands:
  135. Adding new :program:`celery` sub-commands
  136. -----------------------------------------
  137. New commands can be added to the :program:`celery` umbrella command by using
  138. `setuptools entry-points`_.
  139. .. _`setuptools entry-points`:
  140. http://reinout.vanrees.org/weblog/2010/01/06/zest-releaser-entry-points.html
  141. Entry-points is special metadata that can be added to your packages ``setup.py`` program,
  142. and then after installation, read from the system using the :mod:`pkg_resources` module.
  143. Celery recognizes ``celery.commands`` entry-points to install additional
  144. subcommands, where the value of the entry-point must point to a valid subclass
  145. of :class:`celery.bin.base.Command`. Sadly there is limited documentation,
  146. but you can find inspiration from the various commands in the
  147. :mod:`celery.bin` package.
  148. This is how the Flower_ monitoring extension adds the :program:`celery flower` command,
  149. by adding an entry-point in :file:`setup.py`:
  150. .. code-block:: python
  151. setup(
  152. name='flower',
  153. entry_points={
  154. 'celery.commands': [
  155. 'flower = flower.command.FlowerCommand',
  156. ],
  157. }
  158. )
  159. .. _Flower: http://pypi.python.org/pypi/flower
  160. The command definition is in two parts separated by the equal sign, where the
  161. first part is the name of the subcommand (flower), then the fully qualified
  162. module path to the class that implements the command
  163. (``flower.command.FlowerCommand``).
  164. In the module :file:`flower/command.py`, the command class is defined
  165. something like this:
  166. .. code-block:: python
  167. from celery.bin.base import Command, Option
  168. class FlowerCommand(Command):
  169. def get_options(self):
  170. return (
  171. Option('--port', default=8888, type='int',
  172. help='Webserver port',
  173. ),
  174. Option('--debug', action='store_true'),
  175. )
  176. def run(self, port=None, debug=False, **kwargs):
  177. print('Running our command')