|
@@ -21,6 +21,11 @@
|
|
|
|
|
|
Path to pidfile.
|
|
|
|
|
|
+.. cmdoption:: -B, --beat
|
|
|
+
|
|
|
+ Also run the ``celerybeat`` periodic task scheduler. Please note that
|
|
|
+ there must only be one instance of this service.
|
|
|
+
|
|
|
.. cmdoption:: -s, --statistics
|
|
|
|
|
|
Turn on reporting of statistics (remember to flush the statistics message
|
|
@@ -63,28 +68,17 @@
|
|
|
"""
|
|
|
import os
|
|
|
import sys
|
|
|
-CAN_DETACH = True
|
|
|
-try:
|
|
|
- import resource
|
|
|
-except ImportError:
|
|
|
- CAN_DETACH = False
|
|
|
-
|
|
|
-from celery.loaders import current_loader
|
|
|
-from celery.loaders import settings
|
|
|
+import multiprocessing
|
|
|
+import traceback
|
|
|
+import optparse
|
|
|
from celery import __version__
|
|
|
-from celery.supervisor import OFASupervisor
|
|
|
-from celery.log import emergency_error
|
|
|
-from celery.conf import LOG_LEVELS, DAEMON_LOG_FILE, DAEMON_LOG_LEVEL
|
|
|
-from celery.conf import DAEMON_CONCURRENCY, DAEMON_PID_FILE
|
|
|
from celery import conf
|
|
|
-from celery import discovery
|
|
|
+from celery import platform
|
|
|
+from celery.log import emergency_error
|
|
|
from celery.task import discard_all
|
|
|
from celery.worker import WorkController
|
|
|
-import signal
|
|
|
-import multiprocessing
|
|
|
-import traceback
|
|
|
-import optparse
|
|
|
-import atexit
|
|
|
+from celery.loaders import current_loader, settings
|
|
|
+from celery.supervisor import OFASupervisor
|
|
|
|
|
|
USE_STATISTICS = getattr(settings, "CELERY_STATISTICS", False)
|
|
|
# Make sure the setting exists.
|
|
@@ -97,10 +91,12 @@ Configuration ->
|
|
|
* Consumer -> Queue:%(consumer_queue)s Routing:%(consumer_rkey)s
|
|
|
* Concurrency -> %(concurrency)s
|
|
|
* Statistics -> %(statistics)s
|
|
|
+ * Celerybeat -> %(celerybeat)s
|
|
|
""".strip()
|
|
|
|
|
|
OPTION_LIST = (
|
|
|
- optparse.make_option('-c', '--concurrency', default=DAEMON_CONCURRENCY,
|
|
|
+ optparse.make_option('-c', '--concurrency',
|
|
|
+ default=conf.DAEMON_CONCURRENCY,
|
|
|
action="store", dest="concurrency", type="int",
|
|
|
help="Number of child processes processing the queue."),
|
|
|
optparse.make_option('--discard', default=False,
|
|
@@ -111,15 +107,19 @@ OPTION_LIST = (
|
|
|
optparse.make_option('-s', '--statistics', default=USE_STATISTICS,
|
|
|
action="store_true", dest="statistics",
|
|
|
help="Collect statistics."),
|
|
|
- optparse.make_option('-f', '--logfile', default=DAEMON_LOG_FILE,
|
|
|
+ optparse.make_option('-f', '--logfile', default=conf.DAEMON_LOG_FILE,
|
|
|
action="store", dest="logfile",
|
|
|
help="Path to log file."),
|
|
|
- optparse.make_option('-l', '--loglevel', default=DAEMON_LOG_LEVEL,
|
|
|
+ optparse.make_option('-l', '--loglevel', default=conf.DAEMON_LOG_LEVEL,
|
|
|
action="store", dest="loglevel",
|
|
|
help="Choose between DEBUG/INFO/WARNING/ERROR/CRITICAL/FATAL."),
|
|
|
- optparse.make_option('-p', '--pidfile', default=DAEMON_PID_FILE,
|
|
|
+ optparse.make_option('-p', '--pidfile', default=conf.DAEMON_PID_FILE,
|
|
|
action="store", dest="pidfile",
|
|
|
help="Path to pidfile."),
|
|
|
+ optparse.make_option('-B', '--beat', default=False,
|
|
|
+ action="store_true", dest="run_clockservice",
|
|
|
+ help="Also run the celerybeat periodic task scheduler. \
|
|
|
+ Please note that only one instance must be running."),
|
|
|
optparse.make_option('-d', '--detach', '--daemon', default=False,
|
|
|
action="store_true", dest="detach",
|
|
|
help="Run in the background as a daemon."),
|
|
@@ -144,56 +144,13 @@ OPTION_LIST = (
|
|
|
)
|
|
|
|
|
|
|
|
|
-def acquire_pidlock(pidfile):
|
|
|
- """Get the :class:`daemon.pidlockfile.PIDLockFile` handler for
|
|
|
- ``pidfile``.
|
|
|
-
|
|
|
- If the ``pidfile`` already exists, but the process is not running the
|
|
|
- ``pidfile`` will be removed, a ``"stale pidfile"`` message is emitted
|
|
|
- and execution continues as normally. However, if the process is still
|
|
|
- running the program will exit complaning that the program is already
|
|
|
- running in the background somewhere.
|
|
|
-
|
|
|
- """
|
|
|
- from daemon.pidlockfile import PIDLockFile
|
|
|
- import errno
|
|
|
- pidlock = PIDLockFile(pidfile)
|
|
|
- if not pidlock.is_locked():
|
|
|
- return pidlock
|
|
|
- pid = pidlock.read_pid()
|
|
|
- try:
|
|
|
- os.kill(pid, 0)
|
|
|
- except os.error, exc:
|
|
|
- if exc.errno == errno.ESRCH:
|
|
|
- sys.stderr.write("Stale pidfile exists. Removing it.\n")
|
|
|
- os.unlink(pidfile)
|
|
|
- return PIDLockFile(pidfile)
|
|
|
- except TypeError, exc:
|
|
|
- sys.stderr.write("Broken pidfile found. Removing it.\n")
|
|
|
- os.unlink(pidfile)
|
|
|
- return PIDLockFile(pidfile)
|
|
|
- else:
|
|
|
- raise SystemExit(
|
|
|
- "ERROR: Pidfile (%s) already exists.\n"
|
|
|
- "Seems celeryd is already running? (PID: %d)" % (
|
|
|
- pidfile, pid))
|
|
|
- return pidlock
|
|
|
-
|
|
|
-
|
|
|
-def run_worker(concurrency=DAEMON_CONCURRENCY, detach=False,
|
|
|
- loglevel=DAEMON_LOG_LEVEL, logfile=DAEMON_LOG_FILE, discard=False,
|
|
|
- pidfile=DAEMON_PID_FILE, umask=0, uid=None, gid=None,
|
|
|
- supervised=False, working_directory=None, chroot=None,
|
|
|
- statistics=None, **kwargs):
|
|
|
+def run_worker(concurrency=conf.DAEMON_CONCURRENCY, detach=False,
|
|
|
+ loglevel=conf.DAEMON_LOG_LEVEL, logfile=conf.DAEMON_LOG_FILE,
|
|
|
+ discard=False, pidfile=conf.DAEMON_PID_FILE, umask=0,
|
|
|
+ uid=None, gid=None, supervised=False, working_directory=None,
|
|
|
+ chroot=None, statistics=None, run_clockservice=False, **kwargs):
|
|
|
"""Starts the celery worker server."""
|
|
|
|
|
|
- # set SIGCLD back to the default SIG_DFL (before python-daemon overrode
|
|
|
- # it) lets the parent wait() for the terminated child process and stops
|
|
|
- # the 'OSError: [Errno 10] No child processes' problem.
|
|
|
-
|
|
|
- if hasattr(signal, "SIGCLD"): # Make sure the platform supports signals.
|
|
|
- signal.signal(signal.SIGCLD, signal.SIG_DFL)
|
|
|
-
|
|
|
print("Celery %s is starting." % __version__)
|
|
|
|
|
|
if statistics is not None:
|
|
@@ -213,7 +170,7 @@ def run_worker(concurrency=DAEMON_CONCURRENCY, detach=False,
|
|
|
|
|
|
# Setup logging
|
|
|
if not isinstance(loglevel, int):
|
|
|
- loglevel = LOG_LEVELS[loglevel.upper()]
|
|
|
+ loglevel = conf.LOG_LEVELS[loglevel.upper()]
|
|
|
if not detach:
|
|
|
logfile = None # log to stderr when not running in the background.
|
|
|
|
|
@@ -238,35 +195,18 @@ def run_worker(concurrency=DAEMON_CONCURRENCY, detach=False,
|
|
|
"loglevel": loglevel,
|
|
|
"pidfile": pidfile,
|
|
|
"statistics": settings.CELERY_STATISTICS and "ON" or "OFF",
|
|
|
+ "celerybeat": run_clockservice and "ON" or "OFF",
|
|
|
})
|
|
|
|
|
|
print("Celery has started.")
|
|
|
if detach:
|
|
|
- if not CAN_DETACH:
|
|
|
- raise RuntimeError(
|
|
|
- "This operating system doesn't support detach. ")
|
|
|
- from daemon import DaemonContext
|
|
|
from celery.log import setup_logger, redirect_stdouts_to_logger
|
|
|
-
|
|
|
- # Since without stderr any errors will be silently suppressed,
|
|
|
- # we need to know that we have access to the logfile
|
|
|
- if logfile:
|
|
|
- open(logfile, "a").close()
|
|
|
-
|
|
|
- pidlock = acquire_pidlock(pidfile)
|
|
|
- if umask is None:
|
|
|
- umask = 0
|
|
|
- if uid is None:
|
|
|
- uid = os.geteuid()
|
|
|
- if gid is None:
|
|
|
- gid = os.getegid()
|
|
|
- working_directory = working_directory or os.getcwd()
|
|
|
- context = DaemonContext(chroot_directory=chroot,
|
|
|
- working_directory=working_directory,
|
|
|
- umask=umask,
|
|
|
- pidfile=pidlock,
|
|
|
- uid=uid,
|
|
|
- gid=gid)
|
|
|
+ context = platform.create_daemon_context(logfile, pidfile,
|
|
|
+ chroot_directory=chroot,
|
|
|
+ working_directory=working_directory,
|
|
|
+ umask=umask,
|
|
|
+ uid=uid,
|
|
|
+ gid=gid)
|
|
|
context.open()
|
|
|
logger = setup_logger(loglevel, logfile)
|
|
|
redirect_stdouts_to_logger(logger, loglevel)
|
|
@@ -279,11 +219,12 @@ def run_worker(concurrency=DAEMON_CONCURRENCY, detach=False,
|
|
|
worker = WorkController(concurrency=concurrency,
|
|
|
loglevel=loglevel,
|
|
|
logfile=logfile,
|
|
|
+ embed_clockservice=run_clockservice,
|
|
|
is_detached=detach)
|
|
|
|
|
|
# Install signal handler that restarts celeryd on SIGHUP,
|
|
|
# (only on POSIX systems)
|
|
|
- install_restart_signal_handler(worker)
|
|
|
+ install_worker_restart_handler(worker)
|
|
|
|
|
|
try:
|
|
|
worker.start()
|
|
@@ -302,14 +243,9 @@ def run_worker(concurrency=DAEMON_CONCURRENCY, detach=False,
|
|
|
raise
|
|
|
|
|
|
|
|
|
-def install_restart_signal_handler(worker):
|
|
|
- """Installs a signal handler that restarts the current program
|
|
|
- when it receives the ``SIGHUP`` signal.
|
|
|
- """
|
|
|
- if not hasattr(signal, "SIGHUP"):
|
|
|
- return # platform is not POSIX
|
|
|
+def install_worker_restart_handler(worker):
|
|
|
|
|
|
- def restart_self(signum, frame):
|
|
|
+ def restart_worker_sig_handler(signum, frame):
|
|
|
"""Signal handler restarting the current python program."""
|
|
|
worker.logger.info("Restarting celeryd (%s)" % (
|
|
|
" ".join(sys.argv)))
|
|
@@ -322,7 +258,8 @@ def install_restart_signal_handler(worker):
|
|
|
worker.stop()
|
|
|
os.execv(sys.executable, [sys.executable] + sys.argv)
|
|
|
|
|
|
- signal.signal(signal.SIGHUP, restart_self)
|
|
|
+ platform.install_signal_handler("SIGHUP", restart_worker_sig_handler)
|
|
|
+
|
|
|
|
|
|
|
|
|
def parse_options(arguments):
|