你如何在Python中创build一个守护进程?
在Google上search会显示x2代码片段。 第一个结果是这个代码配方有很多的文档和解释,以及下面的一些有用的讨论。
但是, 另一个代码示例虽然不包含太多文档,但包含用于传递诸如启动,停止和重新启动等命令的示例代码。 它还创build了一个PID文件,可以方便地检查守护进程是否已经在运行等。
这些示例都解释了如何创build守护进程。 还有什么需要考虑的吗? 一个样本比其他样本更好,为什么?
Sander Marechal的代码示例优于最初发布于2004年的原始代码 。我曾经贡献过Pyro的一个守护进程,但是如果必须这样做的话,可能会使用Sander的代码。
当成为一个行为良好的守护进程时,有很多事情需要处理 :
-
防止核心转储(许多守护进程以root身份运行,核心转储可能包含敏感信息)
-
在
chroot
监控下正确运行 -
为用例适当地设置UID,GID,工作目录,umask和其他进程参数
-
放弃boost的
suid
,sgid
特权 -
closures所有打开的文件描述符,具体取决于用例
-
如果在已经分离的上下文(如
init
,inetd
等)中启动,则行为正确 -
设置明智的守护进程行为的信号处理程序,而且还会根据用例确定具体的处理程序
-
redirect标准stream
stdin
,stdout
,stderr
因为守护进程不再有控制terminal -
处理PID文件作为一个合作咨询锁,这是一个蠕虫本身的许多矛盾但有效的方式来performance自己
-
当进程终止时允许正确的清理
-
实际上成为一个没有导致僵尸的守护进程
其中一些是标准的 ,正如Unix规范文献( 在UNIX环境下的高级编程, W. Richard Stevens,Addison-Wesley,1992)中所描述的。 其他如streamredirect和PID文件处理是大多数守护进程用户期望的常规行为,但是不那么标准化。
所有这些都由PEP 3143 “标准守护进程库”规范覆盖。 python-daemon参考实现在Python 2.7或更高版本以及Python 3.2或更高版本上运行。
当我开发一个新的守护进程应用程序时,这是我开始使用的基本的“Howdy World”Python守护进程。
#!/usr/bin/python import time from daemon import runner class App(): def __init__(self): self.stdin_path = '/dev/null' self.stdout_path = '/dev/tty' self.stderr_path = '/dev/tty' self.pidfile_path = '/tmp/foo.pid' self.pidfile_timeout = 5 def run(self): while True: print("Howdy! Gig'em! Whoop!") time.sleep(10) app = App() daemon_runner = runner.DaemonRunner(app) daemon_runner.do_action()
请注意,您将需要python-deaemon
库。 在Ubuntu中,您可以:
sudo apt-get install python-daemon
然后用./howdy.py start
,用./howdy.py stop
。
请注意python-daemon包,它解决了开箱即用的守护进程背后的许多问题。
其他function(Debian软件包描述):
- 将进程分离到自己的进程组中。
- 设置适合在chroot中运行的进程环境。
- 放弃suid和sgid特权。
- closures所有打开的文件描述符。
- 更改工作目录,uid,gid和umask。
- 设置适当的信号处理程序。
- 打开stdin,stdout和stderr的新文件描述符。
- pipe理指定的PIDlocking文件。
- 为退出处理注册清理函数。
另一种方法是创build一个普通的非守护进程的Python程序,然后使用supervisord从外部守护进程。 这可以节省很多头痛,并且是* nix和便携式的。
可能不是问题的直接答案,但systemd可以用来作为守护程序运行你的应用程序。 这里是一个例子:
[Unit] Description=Python daemon After=syslog.target After=network.target [Service] Type=simple User=<run as user> Group=<run as group group> ExecStart=/usr/bin/python <python script home>/script.py # Give the script some time to startup TimeoutSec=300 [Install] WantedBy=multi-user.target
我更喜欢这种方法,因为很多工作都是为你完成的,然后你的守护进程脚本的行为和你系统的其他部分相似。
-Orby
这里是一个相对较新的Python模块,在黑客新闻中popup。 看起来非常有用,可以用来从脚本内部将python脚本转换为守护进程模式。 链接
由于python-daemon尚未支持python 3.x,并且可以从邮件列表中读取什么,所以可能永远不会,我已经写了一个PEP 3143的新实现: pep3143daemon
pep3143守护进程至less应该支持python 2.6,2.7和3.x
它还包含一个PidFile类。
该库只依赖于标准库和六个模块。
它可以用来替代python守护进程。
这里是文档 。
在python中进行守护进程时,还需要考虑一件事情:
如果您正在使用python 日志logging,并且想要在守护进程之后继续使用它,请确保在处理程序(特别是文件处理程序)上调用close()
)。
如果你不这样做,处理程序仍然可以认为它已经打开了文件,并且你的消息将会消失 – 换句话说,确保logging器知道它的文件已经closures了!
这个假设是在你守护进程完全closures了所有打开的文件描述符的情况下 – 而不是closures所有日志文件(但是closures所有文件然后重新打开你想要的通常更简单)。
这个函数将把一个应用程序转换成一个守护进程:
import sys import os def daemonize(): try: pid = os.fork() if pid > 0: # exit first parent sys.exit(0) except OSError as err: sys.stderr.write('_Fork #1 failed: {0}\n'.format(err)) sys.exit(1) # decouple from parent environment os.chdir('/') os.setsid() os.umask(0) # do second fork try: pid = os.fork() if pid > 0: # exit from second parent sys.exit(0) except OSError as err: sys.stderr.write('_Fork #2 failed: {0}\n'.format(err)) sys.exit(1) # redirect standard file descriptors sys.stdout.flush() sys.stderr.flush() si = open(os.devnull, 'r') so = open(os.devnull, 'w') se = open(os.devnull, 'w') os.dup2(si.fileno(), sys.stdin.fileno()) os.dup2(so.fileno(), sys.stdout.fileno()) os.dup2(se.fileno(), sys.stderr.fileno())
恐怕@Dustin提到的守护进程模块不适合我。 相反,我安装了python守护进程,并使用下面的代码:
# filename myDaemon.py import sys import daemon sys.path.append('/home/ubuntu/samplemodule') # till __init__.py from samplemodule import moduleclass with daemon.DaemonContext(): moduleclass.do_running() # I have do_running() function and whatever I was doing in __main__() in module.py I copied in it.
运行很容易
> python myDaemon.py
为了完整起见,这里是samplemodule目录的内容
>ls samplemodule __init__.py __init__.pyc moduleclass.py
moduleclass.py的内容可以是
class moduleclass(): ... def do_running(): m = moduleclass() # do whatever daemon is required to do.
我修改了Sander Marechal的代码示例中的几行(在接受的答案中由@JeffBauer提到),以添加在守护进程停止之前执行的quit()
方法。 这有时非常有用。
这里是。
注意:我不使用“python-daemon”模块,因为文档仍然丢失(另见许多其他问题),而且比较模糊(如何从命令行启动/停止守护进程)?
使用Python创build守护进程的最简单方法是使用Twisted事件驱动的框架。 它为你处理守护进程所需的所有东西。 它使用Reactor模式来处理并发请求。
80%的时间,当人们说“守护进程”,他们只需要一台服务器。 由于这个问题在这个问题上完全不清楚,所以很难说可能的答案领域是什么。 由于服务器是足够的,从那里开始。 如果实际需要一个真正的“守护进程”(这很less见),请阅读nohup
作为服务器守护进程的一种方式。
直到真正需要实际守护进程的时候,才写一个简单的服务器。
也看看WSGI参考实现。
也看看简单的HTTP服务器 。
“还有什么需要考虑的吗?”是的。 大约一百万件事 什么协议? 有多less个请求? 每个请求需要多长时间? 他们多久会抵达? 你会使用一个专门的过程? 主题? subprocess? 编写一个守护进程是一个很大的工作。