如何在Django中运行自己的守护进程?

20 投票
4 回答
17939 浏览
提问于 2025-04-17 06:27

在我的Django项目中,我需要在后台重复进行一些处理。这些处理需要访问Django的一些功能,所以我把它们放进了Django的命令里,并作为定时任务(cronjob)来运行。现在我发现,有些处理需要更频繁地执行(因为定时任务的限制是最多每分钟执行一次)。另一个问题是,我无法很好地控制,避免同一个命令同时运行。这种情况发生在某个处理过程需要的时间超过了一分钟的时候。我觉得我应该把它们当作守护进程来运行,但我在寻找一种纯粹的方法来用Django实现这个。你有没有遇到过这个问题,或者知道什么干净的解决方案吗?

4 个回答

2

我在理解Celery官网的文档时遇到了一些困难。不过我找到了一些不错的资料,这个网站解释得很清楚。我在一个运行CentOS 6.2的系统上成功配置了django-1.5、Celery-3.0.17和sqlite3。唯一的问题是找不到需要修改的设置模块,我需要把它改成“myprojectname.settings”。

步骤1。 在/etc/default/celeryd目录下创建以下脚本。请注意,你需要根据自己的系统修改一些内容。

# Name of nodes to start, here we have a single node
CELERYD_NODES="w1"

# Where to chdir at start.
CELERYD_CHDIR="/var/www/some_folder/Myproject/"

# Python interpreter from environment, if using virtualenv
ENV_PYTHON="/somewhere/.virtualenvs/MyProject/bin/python"

# How to call "manage.py celeryd_multi"
CELERYD_MULTI="$ENV_PYTHON $CELERYD_CHDIR/manage.py celeryd_multi"

# How to call "manage.py celeryctl"
CELERYCTL="$ENV_PYTHON $CELERYD_CHDIR/manage.py celeryctl"

# Extra arguments to celeryd
CELERYD_OPTS="--time-limit=300 --concurrency=8"

# Name of the celery config module, don't change this.
CELERY_CONFIG_MODULE="celeryconfig"

# %n will be replaced with the nodename.
CELERYD_LOG_FILE="/var/log/celery/%n.log"
CELERYD_PID_FILE="/var/run/celery/%n.pid"

# Workers should run as an unprivileged user.
CELERYD_USER="celery"
CELERYD_GROUP="celery"

# Set any other env vars here too!
PROJET_ENV="PRODUCTION"

# Name of the projects settings module.
# in this case is just settings and not the full path because it will change the dir to
# the project folder first.
export DJANGO_SETTINGS_MODULE="settings"

步骤2。在/etc/default/celeryd目录下创建下面的脚本,并使用以下命令修改它的权限。

chmod +x /etc/init.d/celeryd 

这个脚本不需要修改。来源

#!/bin/sh -e
# ============================================
#  celeryd - Starts the Celery worker daemon.
# ============================================
#
# :Usage: /etc/init.d/celeryd {start|stop|force-reload|restart|try-restart|status}
# :Configuration file: /etc/default/celeryd
#
# See http://docs.celeryq.org/en/latest/cookbook/daemonizing.html#init-script-celeryd


### BEGIN INIT INFO
# Provides:          celeryd
# Required-Start:    $network $local_fs $remote_fs
# Required-Stop:     $network $local_fs $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: celery task worker daemon
### END INIT INFO

#set -e

DEFAULT_PID_FILE="/var/run/celeryd@%n.pid"
DEFAULT_LOG_FILE="/var/log/celeryd@%n.log"
DEFAULT_LOG_LEVEL="INFO"
DEFAULT_NODES="celery"
DEFAULT_CELERYD="-m celery.bin.celeryd_detach"

# /etc/init.d/celeryd: start and stop the celery task worker daemon.

CELERY_DEFAULTS=${CELERY_DEFAULTS:-"/etc/default/celeryd"}

test -f "$CELERY_DEFAULTS" && . "$CELERY_DEFAULTS"
if [ -f "/etc/default/celeryd" ]; then
    . /etc/default/celeryd
fi

CELERYD_PID_FILE=${CELERYD_PID_FILE:-${CELERYD_PIDFILE:-$DEFAULT_PID_FILE}}
CELERYD_LOG_FILE=${CELERYD_LOG_FILE:-${CELERYD_LOGFILE:-$DEFAULT_LOG_FILE}}
CELERYD_LOG_LEVEL=${CELERYD_LOG_LEVEL:-${CELERYD_LOGLEVEL:-$DEFAULT_LOG_LEVEL}}
CELERYD_MULTI=${CELERYD_MULTI:-"celeryd-multi"}
CELERYD=${CELERYD:-$DEFAULT_CELERYD}
CELERYCTL=${CELERYCTL:="celeryctl"}
CELERYD_NODES=${CELERYD_NODES:-$DEFAULT_NODES}

export CELERY_LOADER

if [ -n "$2" ]; then
    CELERYD_OPTS="$CELERYD_OPTS $2"
fi

CELERYD_LOG_DIR=`dirname $CELERYD_LOG_FILE`
CELERYD_PID_DIR=`dirname $CELERYD_PID_FILE`
if [ ! -d "$CELERYD_LOG_DIR" ]; then
    mkdir -p $CELERYD_LOG_DIR
fi
if [ ! -d "$CELERYD_PID_DIR" ]; then
    mkdir -p $CELERYD_PID_DIR
fi

# Extra start-stop-daemon options, like user/group.
if [ -n "$CELERYD_USER" ]; then
    DAEMON_OPTS="$DAEMON_OPTS --uid=$CELERYD_USER"
    chown "$CELERYD_USER" $CELERYD_LOG_DIR $CELERYD_PID_DIR
fi
if [ -n "$CELERYD_GROUP" ]; then
    DAEMON_OPTS="$DAEMON_OPTS --gid=$CELERYD_GROUP"
    chgrp "$CELERYD_GROUP" $CELERYD_LOG_DIR $CELERYD_PID_DIR
fi

if [ -n "$CELERYD_CHDIR" ]; then
    DAEMON_OPTS="$DAEMON_OPTS --workdir=\"$CELERYD_CHDIR\""
fi


check_dev_null() {
    if [ ! -c /dev/null ]; then
        echo "/dev/null is not a character device!"
        exit 1
    fi
}


export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"


stop_workers () {
    $CELERYD_MULTI stop $CELERYD_NODES --pidfile="$CELERYD_PID_FILE"
}


start_workers () {
    $CELERYD_MULTI start $CELERYD_NODES $DAEMON_OPTS        \
                         --pidfile="$CELERYD_PID_FILE"      \
                         --logfile="$CELERYD_LOG_FILE"      \
                         --loglevel="$CELERYD_LOG_LEVEL"    \
                         --cmd="$CELERYD"                   \
                         $CELERYD_OPTS
}


restart_workers () {
    $CELERYD_MULTI restart $CELERYD_NODES $DAEMON_OPTS      \
                           --pidfile="$CELERYD_PID_FILE"    \
                           --logfile="$CELERYD_LOG_FILE"    \
                           --loglevel="$CELERYD_LOG_LEVEL"  \
                           --cmd="$CELERYD"                 \
                           $CELERYD_OPTS
}



case "$1" in
    start)
        check_dev_null
        start_workers
    ;;

    stop)
        check_dev_null
        stop_workers
    ;;

    reload|force-reload)
        echo "Use restart"
    ;;

    status)
        $CELERYCTL status $CELERYCTL_OPTS
    ;;

    restart)
        check_dev_null
        restart_workers
    ;;

    try-restart)
        check_dev_null
        restart_workers
    ;;

    *)
        echo "Usage: /etc/init.d/celeryd {start|stop|restart|try-restart|kill}"
        exit 1
    ;;
esac

exit 0

步骤3。使用这些命令来启动、停止等操作这个脚本。

# to start celeryd
/etc/init.d/celeryd start

# to stop
/etc/init.d/celeryd stop

# see the status
/etc/init.d/celeryd status

# print the log in the screen
cat /var/log/celery/w1.log  

如果你遇到问题,网站上有很多评论和其他建议。希望这个网站能长期保持在线。

3

我们采取了更简单的方法——写一个普通的脚本,里面有一个无限循环,这个循环会遍历一个查询结果,然后用 supervise 来管理它,让它像个后台程序一样运行。基本上,这就是让后台程序运行所需要的全部内容:

$ sudo apt-get install daemontools daemontools-run
$ mkdir /etc/service/sendmsevad
$ echo -> /etc/service/sendmsevad/run
#!/bin/bash
exec /usr/local/bin/sendmsgd
$ sudo svc -d  /etc/service/sendmsgd
$ sudo svc -u  /etc/service/sendmsgd
$ sudo svstat /etc/service/sendmsgd
/etc/service/sendmsg: up (pid 10521) 479 seconds

想了解更多内容,可以查看这个链接 - 如何让一个普通脚本在Unix中变成后台程序?

现在,/usr/local/bin/sendmsgd 可能看起来像这样:

def main(args=None):
    while True:
        process_messages()
        time.sleep(10)

if __name__ == '__main__':
    import signal
    def signal_handler(signal, frame):
        sys.exit(0)
    signal.signal(signal.SIGINT, signal_handler)

    main(sys.argv)
18

我们在Django中使用Celery进行很多后台处理,具体可以查看http://celeryproject.org/。虽然设置起来需要花一些时间,并且有一点学习难度,但一旦它运行起来,就非常棒。

撰写回答