【问题标题】:Running code when closing a Python daemon关闭 Python 守护程序时运行代码
【发布时间】:2016-11-03 00:06:57
【问题描述】:

我正在使用著名的code referenced here 或here 在 Python 中做一个守护进程,如下所示:

import sys, daemon

class test(daemon.Daemon):
    def run(self):
        self.db = somedb.connect()            # connect to a DB
        self.blah = 127
        with open('blah0.txt', 'w') as f:
            f.write(self.blah)
        # doing lots of things here, modifying self.blah

    def before_stop(self):
        self.db.close()                      # properly close the DB (sync to disk, etc.)
        with open('blah1.txt', 'w') as f:
            f.write(self.blah)

daemon = test(pidfile='_.pid')

if 'start' == sys.argv[1]: 
    daemon.start()
elif 'stop' == sys.argv[1]: 
    daemon.before_stop()               # AttributeError: test instance has no attribute 'blah'
    daemon.stop()

问题是当调用./myscript.py stop 和daemon.before_stop() 时,不再有对self.blah 的引用!

AttributeError: 测试实例没有属性'blah'

因此,使用这种守护程序方法,在停止守护程序之前无法访问守护程序的变量...

问题:如何访问之前的守护类变量:

  • 停在./myscript.py stop

  • 使用 SIGTERM 停止

  • (被杀?)


编辑:已解决,here is a working daemon code with a quit() method.

【问题讨论】:

  • 我建议检查守护进程对象的引用,看看你是否真的在调用之间访问同一个对象。如果需要,请使用您自己的签名。
  • @dcwhcdhw 这正是问题所在,调用之间的对象不同。关闭守护进程时如何处理?

标签: python linux daemon python-daemon


【解决方案1】:

守护进程代码向守护进程发送一个 SIGTERM 信号,要求它停止。如果您希望某些东西由守护进程本身运行,它必须从信号处理程序或从atexit.register 调用的方法运行。

daemonize 方法已经安装了这样的方法,只需从那里调用beforestop:

# this one could be either in a subclass or in a modified base daemeon class
def delpid(self):
        if hasattr(self, 'before_stop'):
                self.before_stop()
        os.remove(self.pidfile)

# this one should be in subclass
def before_stop(self):
    self.db.close()                      # properly close the DB (sync to disk, etc.)
    with open('blah1.txt', 'w') as f:
        f.write(self.blah)

但这还不够! Python 标准库文档中提到了atexit:

当程序被 Python 未处理的信号杀死时,不会调用通过此模块注册的函数

由于进程预计会收到 SIGTERM 信号,因此您必须安装一个处理程序。如一个active state recipe所示,非常简单:只要程序收到信号就停止:

...
from signal import signal, SIGTERM
...

atexit.register(self.delpid)
signal(SIGTERM, lambda signum, stack_frame: exit(1))

【讨论】:

  • 谢谢。我不确定完全理解。您能否发布一个示例,显示在何处放置 self.db.close() 以关闭已在守护进程 run 函数中打开的数据库:self.db = somedb.connect()(请参阅我更新的问题)。
  • @Basj: 因为delpid 在daemonize 中注册了atexit.register,它将在守护进程退出之前被调用。
  • 再次感谢@SergeBallesta。我花了至少 1 个小时在这上面,但它仍然不起作用。你能看到为什么 quit() 没有被执行/blah1.txt 没有被写,当做 python test.py start 然后 python test.py stop ?我按照您的建议修改了daemon.py,所以它应该可以工作。这是两个文件:gget.it/3tvspqh9/test.py、gget.it/y64dirn9/daemon.py
  • Re @SergeBallesta,你认为你可以快速看一下这段代码吗?它仍然不起作用......我认为它对很多人都有用。
  • @Basj:SIGTERM 信号必须由 Python 处理才能调用 atexit 注册方法。查看我的编辑...
【解决方案2】:

当您调用before_stop() 时,Daemon 类不会检索以前的对象,它只是通过 PID 找到进程并使用SIGTERM 杀死。如果你需要一些实例属性,要么在 __init__() 中初始化它们,要么将它们写在一些临时文件中,以便稍后在你的 before_stop() 实现中读取它们。

作为一般规则,您应该始终初始化 __init__() 内的所有实例绑定属性。

【讨论】:

  • 谢谢。我在问题代码中添加了 2 行:假设我在守护进程启动 self.db = somedb.connect() 时打开了一个数据库。在执行./myscript.py stop 时,如何访问到相同的 self.db 对象以执行self.db.close()?似乎无法访问与以前相同的 self.db 对象?
  • 在我看来,您的做法是错误的:流程管理系统(在您的情况下是 Daemon 类)不应该与您的应用程序逻辑混淆。把它们分开,你会遇到更少的问题。简单地说,您的应用程序在收到SIGTERM 时应该以正确的方式做出反应...关闭任何数据库连接、文件等。只是不要在Daemon 子类中这样做。
猜你喜欢
  • 2017-05-06
  • 2021-11-06
  • 2012-05-19
  • 2019-08-13
  • 2013-11-17
  • 2010-11-27
  • 1970-01-01
  • 2023-03-02
  • 2013-09-14
相关资源
最近更新 更多