如何在BaseHTTPRequestHandler子类中停止BaseHTTPServer.serve_forever()?

69 投票
12 回答
104722 浏览
提问于 2025-04-11 18:00

我在一个单独的线程中运行我的 HTTPServer(使用线程模块,但这个模块没有办法停止线程……),我想在主线程关闭时停止处理请求。

Python的文档说 BaseHTTPServer.HTTPServerSocketServer.TCPServer 的一个子类,后者支持一个 shutdown 方法,但在 HTTPServer 中却没有这个方法。

整个 BaseHTTPServer 模块的文档非常少 :(

12 个回答

29

事件循环会在接收到SIGTERM信号、按下Ctrl+C,或者调用shutdown()时结束。

在调用server_forever()之后,必须调用server_close()来关闭监听的套接字。

import http.server

class StoppableHTTPServer(http.server.HTTPServer):
    def run(self):
        try:
            self.serve_forever()
        except KeyboardInterrupt:
            pass
        finally:
            # Clean-up server (close socket, etc.)
            self.server_close()

这是一个简单的服务器,可以通过用户的操作(比如SIGTERM、Ctrl+C等)来停止:

server = StoppableHTTPServer(("127.0.0.1", 8080),
                             http.server.BaseHTTPRequestHandler)
server.run()

服务器在一个线程中运行:

import threading

server = StoppableHTTPServer(("127.0.0.1", 8080),
                             http.server.BaseHTTPRequestHandler)

# Start processing requests
thread = threading.Thread(None, server.run)
thread.start()

# ... do things ...

# Shutdown server
server.shutdown()
thread.join()
42

还有一种方法可以实现,参考这个链接:http://docs.python.org/2/library/basehttpserver.html#more-examples。这个方法不是一直运行服务器,而是只在满足某个条件的时候继续提供服务。服务器会在每次处理请求之前和之后检查这个条件。举个例子:

import CGIHTTPServer
import BaseHTTPServer

KEEP_RUNNING = True

def keep_running():
    return KEEP_RUNNING

class Handler(CGIHTTPServer.CGIHTTPRequestHandler):
    cgi_directories = ["/cgi-bin"]

httpd = BaseHTTPServer.HTTPServer(("", 8000), Handler)

while keep_running():
    httpd.handle_request()
30

我先说一下,“我可能自己不会这样做,但我以前确实做过”。serve_forever(来自SocketServer.py)这个方法是这样的:

def serve_forever(self):
    """Handle one request at a time until doomsday."""
    while 1:
        self.handle_request()

你可以在子类中把while 1替换成while self.should_be_running,然后可以从其他线程修改这个值。大概是这样的:

def stop_serving_forever(self):
    """Stop handling requests"""
    self.should_be_running = 0
    # Make a fake request to the server, to really force it to stop.
    # Otherwise it will just stop on the next request.
    # (Exercise for the reader.)
    self.make_a_fake_request_to_myself()

补充:我找到了当时用的实际代码:

class StoppableRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):

    stopped = False
    allow_reuse_address = True

    def __init__(self, *args, **kw):
        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, *args, **kw)
        self.register_function(lambda: 'OK', 'ping')

    def serve_forever(self):
        while not self.stopped:
            self.handle_request()

    def force_stop(self):
        self.server_close()
        self.stopped = True
        self.create_dummy_request()

    def create_dummy_request(self):
        server = xmlrpclib.Server('http://%s:%s' % self.server_address)
        server.ping()

撰写回答