将Python的BaseHTTPServer变成守护进程

13
我正在开发一个守护进程,并需要嵌入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个回答

8

在经过一番搜索后,我终于找到了这份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()

大多数情况下,这发生在我分叉并最终解决了我的问题之后。

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文件等。但这些都不是问题的范畴。
特别地,问题的范畴之外的是:一旦它成为守护进程(必然脱离任何控制终端),我如何停止守护进程?这取决于你的决策,作为定义程序行为的一部分。

1
很好地启动了守护进程,但我该如何使用启动和停止命令?到目前为止,我无法停止守护进程。 - anno1337
你如何停止这个守护进程? - MohitC
我再重申一遍:在程序成为守护进程后,你需要决定如何与程序进行交互。这包括对程序停止方式的决策,因为不再有控制终端。PID 文件、SystemD 服务等都是可行的选择,但选择权在你手中,这并不是成为守护进程的必要条件。 - bignose

2
你需要先实例化一个HTTPServer。但在代码中并没有告诉它开始提供服务。在你的子进程中,尝试调用server.serve_forever()来启动服务。
参考链接: 点击这里

实际上,在 fork 之后我正在运行它,根据我的调试输出,在第二个 fork 之后,我正在执行代码。 - Gavin M. Roy

1
一个对我有效的简单解决方案是重写 BaseHTTPRequestHandler 方法 log_message(),这样我们就可以防止在 stdout 中写入任何内容,并避免在守护进程时出现问题。
class CustomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    def log_message(self, format, *args):
            pass

...
rest of custom class code
...

0

自从我最初发布以来,这个问题已经引起了很多回答,所以我想分享一些信息。

输出问题与日志模块的默认处理程序使用StreamHandler有关。处理此问题的最佳方法是创建自己的处理程序。如果您想使用默认的日志记录模块,则可以像这样操作:

# Get the default logger
default_logger = logging.getLogger('')

# Add the handler
default_logger.addHandler(myotherhandler)

# Remove the default stream handler
for handler in default_logger.handlers:
    if isinstance(handler, logging.StreamHandler):
        default_logger.removeHandler(handler)

此外,我现在已经开始使用非常好的Tornado项目来作为我的嵌入式HTTP服务器。


0

不要自己编写守护进程,而是直接使用daemontools或其他类似脚本。最好将此过程从脚本中分离出来。

另外,最好的选择是:不要使用BaseHTTPServer。它真的很糟糕。Python有许多优秀的HTTP服务器,如cherrypypaste。这两个都包含了可直接使用的守护进程脚本。


2
为什么它被认为是“坏”的?请注意,这是谷歌搜索结果中排名靠前的内容之一...Python 有大量预建的 HTTP 库,因此更多关于这些库的描述将是非常棒的。 - nsij22

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接