Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

components.py 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. # -*- coding: utf-8 -*-
  2. """
  3. celery.worker.components
  4. ~~~~~~~~~~~~~~~~~~~~~~~~
  5. Default worker bootsteps.
  6. """
  7. from __future__ import absolute_import
  8. import atexit
  9. import warnings
  10. from kombu.async import Hub as _Hub, get_event_loop, set_event_loop
  11. from kombu.async.semaphore import DummyLock, LaxBoundedSemaphore
  12. from kombu.async.timer import Timer as _Timer
  13. from celery import bootsteps
  14. from celery._state import _set_task_join_will_block
  15. from celery.exceptions import ImproperlyConfigured
  16. from celery.five import string_t
  17. from celery.utils.log import worker_logger as logger
  18. __all__ = ['Timer', 'Hub', 'Queues', 'Pool', 'Beat', 'StateDB', 'Consumer']
  19. ERR_B_GREEN = """\
  20. -B option doesn't work with eventlet/gevent pools: \
  21. use standalone beat instead.\
  22. """
  23. W_POOL_SETTING = """
  24. The CELERYD_POOL setting should not be used to select the eventlet/gevent
  25. pools, instead you *must use the -P* argument so that patches are applied
  26. as early as possible.
  27. """
  28. class Timer(bootsteps.Step):
  29. """This step initializes the internal timer used by the worker."""
  30. def create(self, w):
  31. if w.use_eventloop:
  32. # does not use dedicated timer thread.
  33. w.timer = _Timer(max_interval=10.0)
  34. else:
  35. if not w.timer_cls:
  36. # Default Timer is set by the pool, as e.g. eventlet
  37. # needs a custom implementation.
  38. w.timer_cls = w.pool_cls.Timer
  39. w.timer = self.instantiate(w.timer_cls,
  40. max_interval=w.timer_precision,
  41. on_timer_error=self.on_timer_error,
  42. on_timer_tick=self.on_timer_tick)
  43. def on_timer_error(self, exc):
  44. logger.error('Timer error: %r', exc, exc_info=True)
  45. def on_timer_tick(self, delay):
  46. logger.debug('Timer wake-up! Next eta %s secs.', delay)
  47. class Hub(bootsteps.StartStopStep):
  48. requires = (Timer, )
  49. def __init__(self, w, **kwargs):
  50. w.hub = None
  51. def include_if(self, w):
  52. return w.use_eventloop
  53. def create(self, w):
  54. w.hub = get_event_loop()
  55. if w.hub is None:
  56. w.hub = set_event_loop(_Hub(w.timer))
  57. self._patch_thread_primitives(w)
  58. return self
  59. def start(self, w):
  60. pass
  61. def stop(self, w):
  62. w.hub.close()
  63. def terminate(self, w):
  64. w.hub.close()
  65. def _patch_thread_primitives(self, w):
  66. # make clock use dummy lock
  67. w.app.clock.mutex = DummyLock()
  68. # multiprocessing's ApplyResult uses this lock.
  69. try:
  70. from billiard import pool
  71. except ImportError:
  72. pass
  73. else:
  74. pool.Lock = DummyLock
  75. class Queues(bootsteps.Step):
  76. """This bootstep initializes the internal queues
  77. used by the worker."""
  78. label = 'Queues (intra)'
  79. requires = (Hub, )
  80. def create(self, w):
  81. w.process_task = w._process_task
  82. if w.use_eventloop:
  83. if w.pool_putlocks and w.pool_cls.uses_semaphore:
  84. w.process_task = w._process_task_sem
  85. class Pool(bootsteps.StartStopStep):
  86. """Bootstep managing the worker pool.
  87. Describes how to initialize the worker pool, and starts and stops
  88. the pool during worker startup/shutdown.
  89. Adds attributes:
  90. * autoscale
  91. * pool
  92. * max_concurrency
  93. * min_concurrency
  94. """
  95. requires = (Queues, )
  96. def __init__(self, w, autoscale=None, autoreload=None,
  97. no_execv=False, optimization=None, **kwargs):
  98. if isinstance(autoscale, string_t):
  99. max_c, _, min_c = autoscale.partition(',')
  100. autoscale = [int(max_c), min_c and int(min_c) or 0]
  101. w.autoscale = autoscale
  102. w.pool = None
  103. w.max_concurrency = None
  104. w.min_concurrency = w.concurrency
  105. w.no_execv = no_execv
  106. if w.autoscale:
  107. w.max_concurrency, w.min_concurrency = w.autoscale
  108. self.autoreload_enabled = autoreload
  109. self.optimization = optimization
  110. def close(self, w):
  111. if w.pool:
  112. w.pool.close()
  113. def terminate(self, w):
  114. if w.pool:
  115. w.pool.terminate()
  116. def create(self, w, semaphore=None, max_restarts=None):
  117. if w.app.conf.CELERYD_POOL in ('eventlet', 'gevent'):
  118. warnings.warn(UserWarning(W_POOL_SETTING))
  119. threaded = not w.use_eventloop
  120. procs = w.min_concurrency
  121. forking_enable = w.no_execv if w.force_execv else True
  122. if not threaded:
  123. semaphore = w.semaphore = LaxBoundedSemaphore(procs)
  124. w._quick_acquire = w.semaphore.acquire
  125. w._quick_release = w.semaphore.release
  126. max_restarts = 100
  127. allow_restart = self.autoreload_enabled or w.pool_restarts
  128. pool = w.pool = self.instantiate(
  129. w.pool_cls, w.min_concurrency,
  130. initargs=(w.app, w.hostname),
  131. maxtasksperchild=w.max_tasks_per_child,
  132. timeout=w.task_time_limit,
  133. soft_timeout=w.task_soft_time_limit,
  134. putlocks=w.pool_putlocks and threaded,
  135. lost_worker_timeout=w.worker_lost_wait,
  136. threads=threaded,
  137. max_restarts=max_restarts,
  138. allow_restart=allow_restart,
  139. forking_enable=forking_enable,
  140. semaphore=semaphore,
  141. sched_strategy=self.optimization,
  142. )
  143. _set_task_join_will_block(pool.task_join_will_block)
  144. return pool
  145. def info(self, w):
  146. return {'pool': w.pool.info if w.pool else 'N/A'}
  147. def register_with_event_loop(self, w, hub):
  148. w.pool.register_with_event_loop(hub)
  149. class Beat(bootsteps.StartStopStep):
  150. """Step used to embed a beat process.
  151. This will only be enabled if the ``beat``
  152. argument is set.
  153. """
  154. label = 'Beat'
  155. conditional = True
  156. def __init__(self, w, beat=False, **kwargs):
  157. self.enabled = w.beat = beat
  158. w.beat = None
  159. def create(self, w):
  160. from celery.beat import EmbeddedService
  161. if w.pool_cls.__module__.endswith(('gevent', 'eventlet')):
  162. raise ImproperlyConfigured(ERR_B_GREEN)
  163. b = w.beat = EmbeddedService(w.app,
  164. schedule_filename=w.schedule_filename,
  165. scheduler_cls=w.scheduler_cls)
  166. return b
  167. class StateDB(bootsteps.Step):
  168. """This bootstep sets up the workers state db if enabled."""
  169. def __init__(self, w, **kwargs):
  170. self.enabled = w.state_db
  171. w._persistence = None
  172. def create(self, w):
  173. w._persistence = w.state.Persistent(w.state, w.state_db, w.app.clock)
  174. atexit.register(w._persistence.save)
  175. class Consumer(bootsteps.StartStopStep):
  176. last = True
  177. def create(self, w):
  178. if w.max_concurrency:
  179. prefetch_count = max(w.min_concurrency, 1) * w.prefetch_multiplier
  180. else:
  181. prefetch_count = w.concurrency * w.prefetch_multiplier
  182. c = w.consumer = self.instantiate(
  183. w.consumer_cls, w.process_task,
  184. hostname=w.hostname,
  185. send_events=w.send_events,
  186. init_callback=w.ready_callback,
  187. initial_prefetch_count=prefetch_count,
  188. pool=w.pool,
  189. timer=w.timer,
  190. app=w.app,
  191. controller=w,
  192. hub=w.hub,
  193. worker_options=w.options,
  194. disable_rate_limits=w.disable_rate_limits,
  195. prefetch_multiplier=w.prefetch_multiplier,
  196. )
  197. return c