【问题标题】:Daemonizing python's BaseHTTPServer守护python的BaseHTTPServer
【发布时间】:2010-10-27 16:37:41
【问题描述】:

我正在开发一个需要嵌入 HTTP 服务器的守护程序。我正在尝试使用 BaseHTTPServer 来执行此操作,当我在前台运行它时,它工作正常,但是当我尝试将守护进程分叉到后台时,它停止工作。我的主应用程序继续工作,但 BaseHTTPServer 没有。

我相信这与 BaseHTTPServer 将日志数据发送到 STDOUT 和 STDERR 的事实有关。我将这些重定向到文件。这是代码sn-p:

# 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 显示在我的分叉后台守护程序中,我在分叉后点击代码。

【问题讨论】:

    标签: python daemon basehttpserver


    【解决方案1】:

    以下是使用 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 文件等。不过,这些都超出了问题的范围。

    特别是,它超出了问题的范围:一旦它成为一个守护进程(必须与任何控制终端分离),how do I stop the daemon process?作为定义程序行为的一部分,这由您决定。

    【讨论】:

    • 非常适合启动守护进程,但我如何将它与启动和停止命令一起使用?到目前为止,我无法停止守护进程。
    • 如何停止这个守护进程?
    • 我再说一遍:您需要决定在程序成为守护程序后如何与程序交互。这包括决定如何停止程序,因为不再有控制终端。 PID 文件、SystemD 服务等都是可行的,但选择权在你,它不是守护进程的必要组成部分。
    【解决方案2】:

    经过一番谷歌搜索后,我 finally stumbled over this BaseHTTPServer documentation 之后我得到了:

    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 并最终解决了我的问题之后。

    【讨论】:

      【解决方案3】:

      对我有用的一个简单解决方案是覆盖 BaseHTTPRequestHandler 方法 log_message(),因此我们可以防止在 stdout 中进行任何类型的写入,并避免在妖魔化时出现问题。

      class CustomRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
      
          def log_message(self, format, *args):
                  pass
      
      ...
      rest of custom class code
      ...
      

      【讨论】:

        【解决方案4】:

        自从我最初发布以来,这已经征求了答案,我想我会分享一些信息。

        输出问题与日志记录模块的默认处理程序使用 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 服务器。

        【讨论】:

          【解决方案5】:

          首先实例化一个 HTTPServer。但是您实际上并没有告诉它在任何提供的代码中开始服务。在您的子进程中尝试调用server.serve_forever()

          See this供参考

          【讨论】:

          • 实际上,我是在 fork 之后运行的,根据我的调试输出,在第二个 fork 之后,我正在运行代码。
          【解决方案6】:

          只需使用daemontools 或其他类似脚本,而不是滚动您自己的守护进程。最好不要在脚本中使用它。

          另外,您最好的选择:不要使用 BaseHTTPServer。这真的很糟糕。 python 有很多不错的 HTTP 服务器,即cherrypypaste。两者都包括现成的守护程序脚本。

          【讨论】:

          • 究竟为什么不好?拜托,这是谷歌的顶级结果之一......有大量用于 http 的预构建 python 库,所以对这些库的更多描述也会令人惊叹。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-14
          • 1970-01-01
          • 2012-02-06
          相关资源
          最近更新 更多