Searching on Google揭示了x2代码片段。第一个结果是this code recipe,它有很多文档和解释,以及下面的一些有用的讨论。
但是,another code sample虽然没有包含那么多文档,但包含用于传递命令的示例代码,例如启动,停止和重启。它还会创建一个PID文件,可以方便地检查守护程序是否已在运行等。
这些示例都解释了如何创建守护进程。还有其他事情需要考虑吗?一个样本比另一个好,为什么?
当前解决方案
PEP 3143 (Standard daemon process library)的参考实现现在可以作为python-daemon使用。
历史答案
Sander Marechal的code sample优于原版,最初发布于2004年。我曾经为Pyro贡献了一个守护程序,但是如果我不得不这样做的话可能会使用Sander的代码。
我担心@Dustin提到的守护进程模块对我不起作用。相反,我安装了python-daemon并使用了以下代码:
# 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.
在python中进行守护进程时要考虑的事情:
如果您正在使用python日志记录并且希望在守护进程后继续使用它,请确保在处理程序(尤其是文件处理程序)上调用close()
。
如果你不这样做,处理程序仍然认为它有文件打开,你的消息将消失 - 换句话说,确保记录器知道它的文件已关闭!
这假设你守护你正在不加区别地关闭所有打开的文件描述符 - 而你可以尝试关闭除日志文件之外的所有文件(但通常更简单地关闭所有文件描述符然后重新打开你想要的文件描述符)。
我在Sander Marechal的代码示例中修改了几行(由@JeffBauer在the accepted answer中提到),添加了一个在守护程序停止之前执行的quit()
方法。这有时非常有用。
注意:我没有使用“python-daemon”模块,因为文档仍然缺失(参见许多其他SO问题)并且相当模糊(如何使用此模块从命令行正确启动/停止守护进程?)
使用Python创建守护进程的最简单方法是使用Twisted事件驱动框架。它为您处理守护进程所需的所有内容。它使用Reactor Pattern来处理并发请求。
经过几年和多次尝试(我尝试了这里给出的所有答案,但最后都有一些小缺点),现在我意识到有一种比想要直接从Python启动,停止,重启守护进程更好的方法:改用OS工具。
例如,对于Linux,我这样做是为了启动应用程序,而不是做python myapp start
和python myapp stop
:
screen -S myapp python myapp.py
CTRL+A, D to detach
或screen -dmS myapp python myapp.py
到start and detach it in one command。
然后:
screen -r myapp
再次连接到此终端。一旦进入终端,就可以使用CTRL + C来阻止它。
80%的时候,当人们说“守护进程”时,他们只想要一台服务器。由于在这一点上完全不清楚这个问题,很难说答案的可能领域是什么。由于服务器足够,从那里开始。如果实际需要实际的“守护进程”(这种情况很少见),请阅读nohup
作为守护服务器的方法。
在实际需要实际守护进程之前,只需编写一个简单的服务器即可。
另请参阅WSGI reference实现。
“还有其他事情需要考虑吗?”是的。大约一百万件事。什么协议?请求多少?服务每个请求多长时间?他们多久到达一次?你会使用专门的流程吗?主题?子进程?编写守护进程是一项艰巨的任务。
在成为well-behaved daemon process时,有许多令人讨厌的东西需要照顾:
chroot
监狱内表现正确suid
,sgid
特权init
,inetd
等。stdin
,stdout
,stderr
,因为守护进程不再具有控制终端其中一些是标准的,如规范的Unix文献(UNIX环境中的高级编程,已故的W. Richard Stevens,Addison-Wesley,1992)中所述。其他的,例如流重定向和PID file handling,是大多数守护程序用户期望的常规行为,但标准化程度较低。
所有这些都包含在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-daemon
库。您可以通过以下方式安装:
pip install python-daemon
然后用./howdy.py start
启动它,并用./howdy.py stop
停止它。
请注意python-daemon包,它解决了守护进程背后的许多问题。
它可以实现的其他功能(来自Debian软件包描述):
另一种方法 - 创建一个普通的,非守护进程的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
YapDi是一个相对较新的python模块,出现在Hacker News中。看起来非常有用,可用于从脚本内部将python脚本转换为守护进程模式。
因为python-daemon还没有支持python 3.x,并且从邮件列表上可以阅读的内容,它可能永远不会,我已经编写了PEP 3143的新实现:pep3143daemon
pep3143daemon应该至少支持python 2.6,2.7和3.x.
它还包含一个PidFile类。
该库仅依赖于标准库和六个模块。
它可以用作python-daemon的替代品。
此函数将应用程序转换为守护程序:
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())