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

20
在我的 Django 项目中,我需要在后台重复进行一些处理操作。这些处理操作需要访问 Django 的相关内容,所以我将它们放入 Django 的命令行并作为 cronjob 运行。 现在我意识到,有些处理操作需要更频繁地运行(cronjob 最多每分钟调用一次命令)。另一个问题是我没有足够的控制,无法保护同一时间运行同一个命令。当一个处理操作持续时间超过一分钟时,就会发生这种情况。 我认为应该像守护进程一样运行它们,但我正在寻找使用 Django 纯粹的方法来实现它。 您是否曾经遇到过这个问题或者知道任何干净的解决方案?
4个回答

18

我们使用Celery http://celeryproject.org/来进行Django的后台处理。它需要一些配置工作,并且有一定的学习曲线,但一旦设置成功,就会非常棒。


2
一样的情况。RabbitMQ + Celery + django-celery。 - Jordan
根据您的经验,在Django中,Celery是否对于简单的消息队列有点过度臃肿?我之前研究过它,尽管它运行良好,但我认为它远远超出了我的特定用例要求。您是否同意我的看法或是我忽略了某些问题?使用OP上建议的守护程序是否存在任何缺点? - ChrisC
根据“简单消息队列”的定义 - 是的 ;) - Dmitry B.
1
一个定时任务并不等同于一个守护进程。 - Alex
@lxer,如果您阅读问题标题之外的内容,您会意识到 OP 实际上并不是在询问守护进程,而是要寻找一个更好的替代方案来取代这个 cron 设置。在提供有用答案时,考虑到上下文非常重要,以便回答者能够受益。 - Dmitry B.
我能否使用django_background_tasks实现这种行为? - Divij Sehgal

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)

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"

第二步。在/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

步骤三:使用以下命令启动、停止等脚本。
# 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  

如果您遇到问题,网站上有很多评论和其他建议。希望它能长期保持稳定。

0

你可以尝试使用The Fat Controller,它可以接受任何脚本并将其变为守护进程。它还可以按照指定的时间间隔重复运行脚本,甚至可以不间断地运行,以防止同时运行两个实例。

它完全由C语言编写,非常稳定,设计用于长时间运行 - 无论您自己的脚本崩溃了多少次。而且它非常容易上手。

它还可以做很多其他事情,例如并行运行脚本,甚至根据工作量调整并行实例的数量 - 但我想这超出了您的要求范围。

网站上有很多用例和详细说明。如果您需要进一步帮助,请联系我们或提交支持票据,我会尽快回复您。

网站链接:http://fat-controller.sourceforge.net/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接