将Python的BaseHTTPServer转为守护进程

13 投票
6 回答
18365 浏览
提问于 2025-04-15 11:43

我正在开发一个守护进程,需要在里面嵌入一个HTTP服务器。我尝试使用BaseHTTPServer,运行在前台时一切正常,但当我试图把守护进程放到后台时,它就不工作了。我的主应用程序继续运行,但BaseHTTPServer却不行。

我觉得这可能和BaseHTTPServer把日志数据发送到标准输出(STDOUT)和标准错误(STDERR)有关。我把这些输出重定向到了文件。以下是相关的代码片段:

# Start the HTTP Server
server = HTTPServer((config['HTTPServer']['listen'],config['HTTPServer']['port']),HTTPHandler)

# Fork our process to detach if not told to stay in foreground
if options.foreground is False:
    try:
        pid = os.fork()
        if pid > 0:
            logging.info('Parent process ending.')
            sys.exit(0)            
    except OSError, e:
        sys.stderr.write("Could not fork: %d (%s)\n" % (e.errno, e.strerror))
        sys.exit(1)

    # Second fork to put into daemon mode
    try: 
        pid = os.fork() 
        if pid > 0:
            # exit from second parent, print eventual PID before
            print 'Daemon has started - PID # %d.' % pid
            logging.info('Child forked as PID # %d' % pid)
            sys.exit(0) 
    except OSError, e: 
        sys.stderr.write("Could not fork: %d (%s)\n" % (e.errno, e.strerror))
        sys.exit(1)


    logging.debug('After child fork')

    # Detach from parent environment
    os.chdir('/') 
    os.setsid()
    os.umask(0) 

    # Close stdin       
    sys.stdin.close()

    # Redirect stdout, stderr
    sys.stdout = open('http_access.log', 'w')
    sys.stderr = open('http_errors.log', 'w')    

# Main Thread Object for Stats
threads = []

logging.debug('Kicking off threads')

while ...
  lots of code here
...

server.serve_forever()

我是不是做错了什么,还是说BaseHTTPServer在某种情况下无法变成守护进程?

补充说明:我更新了代码,展示了之前缺失的代码流程,并且在我分叉的后台守护进程中,log.debug显示我在分叉后确实有代码在运行。

6 个回答

2

你首先需要创建一个 HTTPServer 的实例。但是在你提供的代码中,并没有实际告诉它开始提供服务。在你的子进程中,可以尝试调用 server.serve_forever() 来让它一直运行。

参考这个链接 了解更多信息。

4

下面是如何使用 python-daemon 这个库来实现的:

from BaseHTTPServer import (HTTPServer, BaseHTTPRequestHandler)
import contextlib

import daemon

from my_app_config import config

# Make the HTTP Server instance.
server = HTTPServer(
    (config['HTTPServer']['listen'], config['HTTPServer']['port']),
    BaseHTTPRequestHandler)

# Make the context manager for becoming a daemon process.
daemon_context = daemon.DaemonContext()
daemon_context.files_preserve = [server.fileno()]

# Become a daemon process.
with daemon_context:
    server.serve_forever()

像往常一样,作为一个守护进程,你需要决定在它变成守护进程后,如何与这个程序进行交互。比如,你可以注册一个 systemd 服务,或者写一个 PID 文件等等。不过这些都不是这个问题的重点。

特别是,关于这个问题的重点是:一旦它变成了一个守护进程(必然与任何控制终端断开连接),我该如何停止这个守护进程?这就得由你来决定,作为定义程序行为的一部分。

8

经过一番搜索,我终于找到了这个关于 BaseHTTPServer 的文档,链接在这里:BaseHTTPServer 文档。之后,我得到了以下的代码:

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from SocketServer import ThreadingMixIn

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
  """Handle requests in a separate thread."""

server = ThreadedHTTPServer((config['HTTPServer']['listen'],config['HTTPServer']['port']), HTTPHandler)
server.serve_forever()

大部分内容是在我分叉(fork)之后,最终解决了我的问题。

撰写回答