如何使用python后台程序设置后台程序?

2024-05-18 17:51:43 发布

您现在位置:Python中文网/ 问答频道 /正文

我是新来的守护进程,所以抱歉,如果这是一个新问题。

在其他几个答案(例如,this question)中,人们建议应该使用python-daemon包,因为它完全实现了PEP 3143标准。

不幸的是,python守护进程是a bit light on documentation(或者更可能我对知识/经验有点了解。。。)我想我可能遗漏了一些基本的东西。我要做的是:

我有以下资料:

import daemon

logfile = open('daemon.log', 'w')

context = daemon.DaemonContext(stdout = logfile, stderr = logfile)

context.open()

with context:
    do_something_1()
    do_something_2()

问题:如何使用python后台程序设置后台程序,如何启动和停止它?


旁注:

我基本上是在胡乱猜测这里应该如何使用.open()方法,或者是否应该使用.open()——在这一点上,文档并不是很清楚。不管我是否包括在内,同样的事情似乎也会发生。

那么,现在我该怎么办?当我尝试运行此文件时,例如:

python startConsumerDaemons.py

它似乎运行do_something_1(),但不是第二个。而且,它似乎将程序附加到终端窗口。也就是说,stdout没有被重定向,当我关闭终端窗口时,进程被终止。所以,我很确定我在这里做错了什么。。。我应该做些什么呢?

最后,在运行守护进程之后,如何停止/重新启动它(例如,如果我更改了底层代码)?


Tags: 答案终端进程stdoutcontextopenthisdo
3条回答

正如您在'with' statement documentation中所看到的,it语句确实执行了一些与我们的目的相关的“魔术”。具体来说:

The execution of the with statement with one “item” proceeds as follows:

  1. The context expression (the expression given in the with_item) is evaluated to obtain a context manager.

  2. The context manager’s __exit__() is loaded for later use.

  3. he context manager’s __enter__() method is invoked.

  4. If a target was included in the with statement, the return value from __enter__() is assigned to it.

  5. The suite is executed.

  6. The context manager’s __exit__() method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to __exit__(). Otherwise, three None arguments are supplied.

这是什么意思?如果仔细查看the PEP in question,它也可以作为python守护进程文档(而且确实可以得到很大改进),您将看到它实现了__enter__()__exit__()

The class also implements the context manager protocol via __enter__ and __exit__ methods.

__enter__()

Call the instance's open() method, then return the instance.

__exit__(exc_type, exc_value, exc_traceback)

Call the instance's close() method, then return True if the exception was handled or False if it was not.

换句话说,不需要open(),PEP中给出的示例(尽管解释不正确)可以正常工作。虽然with语句确实有意义,但它不保留任何循环,一旦到达其作用域的末尾,它将调用exit(),这在python守护进程中是close()的意思。因此,你需要放置一段时间的真实或你认为无限循环。

关于你的第二个剧本不起作用的表现,我真的不能告诉你,我很惊讶第一个已经起作用了。如果守护进程正在停止,脚本肯定有问题,可以检查consumerDaemonLogFile。(另请注意,您键入了'sderr'-->;'stderr')

此外,您可以在PEP中看到,如果未指定,工作目录属性默认为“/”。如果在脚本中使用相对路径,这可能是问题的根源。

最后,关于最后一个问题,您可以很容易地杀死正在查找其PID的守护进程:

ps ax | grep startConsumerDaemons.py

并发送一个SIGTERM:

kill <pid>

gromain提供的答案确实提供了一种更方便的方法来启动和停止它,使用“daemon.runner()”,但是设置起来要复杂得多。

这就是我所拥有的,对我有用。它还有一个sysv init脚本。Repo is at GitHub,我还有a brief blog post链接到我发现的其他可能的解决方案。

只能有一个守护进程正在运行:它与大多数其他Linux守护进程一样,由PID锁文件管理。阻止它,做

kill `cat /var/run/eg_daemon.pid`

要查看它是否正在运行:

ps -elf | grep `cat /var/run/eg_daemon.pid`

使用PID file子模块,可以自动管理PID文件。当守护进程停止时,pidfile被清除。请查看init脚本的链接GitHub repo。

以下是Python守护程序代码:

#!/usr/bin/env python3.5
import sys
import os
import time
import argparse
import logging
import daemon
from daemon import pidfile

debug_p = False

def do_something(logf):
    ### This does the "work" of the daemon

    logger = logging.getLogger('eg_daemon')
    logger.setLevel(logging.INFO)

    fh = logging.FileHandler(logf)
    fh.setLevel(logging.INFO)

    formatstr = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    formatter = logging.Formatter(formatstr)

    fh.setFormatter(formatter)

    logger.addHandler(fh)

    while True:
        logger.debug("this is a DEBUG message")
        logger.info("this is an INFO message")
        logger.error("this is an ERROR message")
        time.sleep(5)


def start_daemon(pidf, logf):
    ### This launches the daemon in its context

    ### XXX pidfile is a context
    with daemon.DaemonContext(
        working_directory='/var/lib/eg_daemon',
        umask=0o002,
        pidfile=pidfile.TimeoutPIDLockFile(pidf),
        ) as context:
        do_something(logf)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Example daemon in Python")
    parser.add_argument('-p', '--pid-file', default='/var/run/eg_daemon.pid')
    parser.add_argument('-l', '--log-file', default='/var/log/eg_daemon.log')

    args = parser.parse_args()

    start_daemon(pidf=args.pid_file, logf=args.log_file)

为了完整起见,这里是init脚本。请注意,“kill”实际上只是发送POSIX信号的一种方法——请参见信号手册页(7)以了解概述。python守护进程上下文将捕获该信号,干净地结束进程关闭文件描述符,并自动删除PID文件。所以,这真的是一个彻底的终结。

您可以编写代码来捕获SIGUSR1或类似的东西,以便重新加载守护程序配置。编写Python停止守护进程没有任何好处。

#!/bin/bash
#
# eg_daemon      Startup script for eg_daemon
#
# chkconfig: - 87 12
# description: eg_daemon is a dummy Python-based daemon
# config: /etc/eg_daemon/eg_daemon.conf
# config: /etc/sysconfig/eg_daemon
# pidfile: /var/run/eg_daemon.pid
#
### BEGIN INIT INFO
# Provides: eg_daemon
# Required-Start: $local_fs
# Required-Stop: $local_fs
# Short-Description: start and stop eg_daemon server
# Description: eg_daemon is a dummy Python-based daemon
### END INIT INFO

# Source function library.
. /etc/rc.d/init.d/functions

if [ -f /etc/sysconfig/eg_daemon ]; then
        . /etc/sysconfig/eg_daemon
fi

eg_daemon=/var/lib/eg_daemon/eg_daemon.py
prog=eg_daemon
pidfile=${PIDFILE-/var/run/eg_daemon.pid}
logfile=${LOGFILE-/var/log/eg_daemon.log}
RETVAL=0

OPTIONS=""

start() {
        echo -n $"Starting $prog: "

        if [[ -f ${pidfile} ]] ; then
            pid=$( cat $pidfile  )
            isrunning=$( ps -elf | grep  $pid | grep $prog | grep -v grep )

            if [[ -n ${isrunning} ]] ; then
                echo $"$prog already running"
                return 0
            fi
        fi
        $eg_daemon -p $pidfile -l $logfile $OPTIONS
        RETVAL=$?
        [ $RETVAL = 0 ] && success || failure
        echo
        return $RETVAL
}

stop() {
    if [[ -f ${pidfile} ]] ; then
        pid=$( cat $pidfile )
        isrunning=$( ps -elf | grep $pid | grep $prog | grep -v grep | awk '{print $4}' )

        if [[ ${isrunning} -eq ${pid} ]] ; then
            echo -n $"Stopping $prog: "
            kill $pid
        else
            echo -n $"Stopping $prog: "
            success
        fi
        RETVAL=$?
    fi
    echo
    return $RETVAL
}

reload() {
    echo -n $"Reloading $prog: "
    echo
}

# See how we were called.
case "$1" in
  start)
    start
    ;;
  stop)
    stop
    ;;
  status)
    status -p $pidfile $eg_daemon
    RETVAL=$?
    ;;
  restart)
    stop
    start
    ;;
  force-reload|reload)
    reload
    ;;
  *)
    echo $"Usage: $prog {start|stop|restart|force-reload|reload|status}"
    RETVAL=2
esac

exit $RETVAL

一个完整的例子是available here

您应该能够更好地理解python守护进程的内部工作原理。

此外,提供的代码还提供了一个init脚本示例,用于简单地启动/停止守护进程。但是,您可以通过使用参数stop再次调用原始函数来启动/停止它:

python original_func.py stop

相关问题 更多 >

    热门问题