如何在 Python 中创建守护进程?
- 2024-12-09 08:29:00
- admin 原创
- 188
问题描述:
在 Google 上搜索会显示 x2 代码片段。第一个结果是这个代码配方,其中包含大量文档和解释,以及一些有用的讨论。
但是,另一个代码示例虽然没有包含太多文档,但包含传递启动、停止和重启等命令的示例代码。它还创建了一个 PID 文件,可以方便地检查守护进程是否已在运行等。
这两个示例都解释了如何创建守护进程。还有其他需要考虑的事情吗?一个示例是否比另一个示例更好,为什么?
解决方案 1:
当前解决方案
PEP 3143(标准守护进程库)的参考实现现已作为python-daemon提供。
历史答案
Sander Marechal 的代码示例优于最初于 2004 年发布的原始代码。我曾经为 Pyro 贡献过一个守护进程,但如果必须重做的话,我可能会使用 Sander 的代码。
解决方案 2:
在成为一个行为良好的守护进程时,有很多棘手的事情需要处理:
防止核心转储(许多守护进程以 root 身份运行,核心转储可能包含敏感信息)
在监狱里行为端正
chroot
根据用例适当地设置 UID、GID、工作目录、umask 和其他进程参数
放弃
suid
特权sgid
关闭所有打开的文件描述符,根据用例排除
如果在已经分离的上下文中启动,则行为正确,例如
init
,inetd
等。设置信号处理程序以实现合理的守护进程行为,但也根据用例确定特定的处理程序
重定向标准流
stdin
,stdout
因为stderr
守护进程不再具有控制终端将 PID 文件作为协作咨询锁来处理,这本身就是一个很棘手的问题,有许多相互矛盾但有效的行为方式
允许在进程终止时进行适当的清理
实际上成为一个守护进程,而不会导致僵尸进程
其中一些是标准的,如经典 Unix 文献(《UNIX 环境高级编程》,由已故的 W. Richard Stevens 编著,Addison-Wesley,1992 年)中所述。其他一些,如流重定向和PID 文件处理,是大多数守护进程用户期望的常规行为,但标准化程度较低。
所有这些都包含在PEP 3143 “标准守护进程库”规范中。python -daemon参考实现适用于 Python 2.7 或更高版本以及 Python 3.2 或更高版本。
解决方案 3:
这是我在开发新的守护进程应用程序时开始使用的基本“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
。
解决方案 4:
另一种方法是创建一个普通的、非守护进程的 Python 程序,然后使用Supervisord在外部对其进行守护进程化。这可以省去很多麻烦,并且可以在 *nix 和语言之间移植。
解决方案 5:
请注意python-daemon包,它解决了守护进程背后的许多问题。
它还可以实现以下功能(来自 Debian 软件包描述):
将进程分离到其自己的进程组中。
设置适合在 chroot 内运行的进程环境。
放弃 suid 和 sgid 权限。
关闭所有打开的文件描述符。
更改工作目录、uid、gid 和 umask。
设置适当的信号处理程序。
为 stdin、stdout 和 stderr 打开新的文件描述符。
管理指定的PID锁文件。
注册用于退出处理的清理函数。
解决方案 6:
这可能不是问题的直接答案,但 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
我更喜欢这种方法,因为很多工作都是为您完成的,然后您的守护进程脚本的行为与系统的其余部分类似。
解决方案 7:
该函数将把应用程序转换为守护进程:
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}
'.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}
'.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())
解决方案 8:
YapDi是一个 Python 包。它可用于将 Python 脚本从脚本内部转换为守护进程模式。
解决方案 9:
由于 python-daemon 尚未支持 python 3.x,并且从邮件列表中可以看到,它可能永远不会支持,因此我编写了 PEP 3143 的新实现:pep3143daemon
pep3143daemon 至少应支持 python 2.6、2.7 和 3.x
它还包含一个 PidFile 类。
该库仅依赖于标准库和六个模块。
它可以用作 python-daemon 的替代品。
这是文档。
解决方案 10:
恐怕@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.
解决方案 11:
虽然您可能更喜欢 python-daemon 模块提供的纯 Python 解决方案,但是至少在BSD和Linuxdaemon(3)
上有一个函数可以做正确的事情。libc
从 python 调用它很容易:
import ctypes
ctypes.CDLL(None).daemon(0, 0) # Read the man-page for the arguments' meanings
剩下要做的唯一一件事就是创建(并锁定)PID 文件。不过你可以自己处理……
解决方案 12:
在 python 中进行守护进程时还有一件事需要考虑:
如果您正在使用 python日志记录,并且想要在守护进程之后继续使用它,请确保调用close()
处理程序(特别是文件处理程序)。
如果您不这样做,处理程序仍然可以认为它已打开文件,并且您的消息将会消失 - 换句话说,确保记录器知道其文件已关闭!
这假设当您守护进程时,您会不加区分地关闭所有打开的文件描述符 - 相反,您可以尝试关闭除日志文件之外的所有文件(但关闭所有文件然后重新打开您想要的文件通常更简单)。
解决方案 13:
我修改了 Sander Marechal 的代码示例中的几行(@JeffBauer 在接受的答案中提到),以添加quit()
在守护进程停止之前执行的方法。这有时非常有用。
这里是。
注意:我不使用“python-daemon”模块,因为文档仍然缺失(另请参阅许多其他 SO 问题)并且相当晦涩(如何使用此模块从命令行正确启动/停止守护进程?)
解决方案 14:
经过几年和多次尝试(我尝试了这里给出的所有答案,但最后它们都有一些小缺点),现在我意识到有一种比直接从 Python 启动、停止、重新启动守护进程更好的方法:使用操作系统工具。
例如,对于 Linux,我不会执行python myapp start
和python myapp stop
,而是执行以下操作来启动应用程序:
screen -S myapp python myapp.py
# CTRL+A, D to detach
或者用一个命令screen -dmS myapp python myapp.py
来启动和分离它。
然后:
screen -r myapp
再次连接到此终端。进入终端后,可以使用 CTRL+C 停止它。
解决方案 15:
使用 Python 创建守护进程的最简单方法是使用Twisted事件驱动框架。它会为您处理守护进程所需的所有工作。它使用Reactor 模式来处理并发请求。
解决方案 16:
80% 的时间里,当人们说“守护进程”时,他们只是想要一个服务器。由于这个问题在这一点上完全不清楚,很难说答案的可能范围是什么。由于服务器就足够了,那就从那里开始吧。如果确实需要一个真正的“守护进程”(这种情况很少见),请阅读有关nohup
如何将服务器守护进程化的内容。
在真正需要真正的守护进程之前,只需编写一个简单的服务器。
另请查看WSGI 参考实现。
另请查看简单 HTTP 服务器。
“还有其他需要考虑的事情吗?”是的。大约有一百万件事情。什么协议?有多少请求?每个请求的服务时间有多长?它们到达的频率是多少?您会使用专用进程吗?线程?子进程?编写守护进程是一项艰巨的工作。
扫码咨询,免费领取项目管理大礼包!