Python atexit 在不同进程中清理服务器 — 端口仍在使用中

0 投票
2 回答
1181 浏览
提问于 2025-04-16 16:03

我有一些集成测试的代码,它会在一个不同的进程中启动一个HTTP服务器,以便进行调用。这个服务器可能会因为活动而受到影响,所以我希望能随时启动和停止新的实例。

但不幸的是,这个方法没有奏效……我遇到了一个问题,就是我的服务器运行的端口在我的进程退出后仍然被锁定(这意味着如果我快速运行两次测试,第二次会失败,因为端口被锁住了)。

我尝试使用 atexit.register 来绑定关闭方法,但也没有成功。

这是服务器的代码:

from BaseHTTPServer import BaseHTTPRequestHandler
import SocketServer
import atexit

class RestHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/sanitycheck':
            self.send_response(200)
            self.send_header('Content-type', 'application/json')
            self.end_headers()
            self.wfile.write("{ 'text': 'You are sane.' }")
        else:
            self.wfile.write(self.path)

def kill_server(httpd):
    open("/tmp/log", "w").write("KILLING")
    httpd.shutdown()

def start_simple_server(port):
    httpd = SocketServer.TCPServer(("", port), RestHTTPRequestHandler)
    atexit.register(kill_server, httpd)

    httpd.serve_forever()

    return httpd

在/tmp/log中什么都没有写,这让我觉得 atexit 可能没有被调用。

这是我如何实例化服务器的:

p = Process(target=start_simple_server, args=(port,))
p.start()

然后当我完成时,要终止它,我只需调用:

p.terminate()

这确实会杀掉进程,并且根据我的理解应该会触发 atexit 的调用——但它没有 :(

有什么想法吗?

2 个回答

0

我最终采取了一种稍微不同的方法,这个方法受到了David Beazley的一些代码的启发... 这是服务器的代码:

from BaseHTTPServer import BaseHTTPRequestHandler
import SocketServer
import multiprocessing
import cgi
import urlparse

class StoppableHTTPServerProcess(multiprocessing.Process):
    def __init__(self, address, handler):
        multiprocessing.Process.__init__(self)
        self.exit = multiprocessing.Event()

        self.server = StoppableHTTPServer(address, handler)

    def run(self):
        while not self.exit.is_set():
            self.server.handle_request()

    def shutdown(self):
        self.exit.set()

class RestHTTPRequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.wfile.write(self.path)


class StoppableHTTPServer(SocketServer.TCPServer):
    allow_reuse_address = True
    timeout = 0.5

def start_simple_server(port):
    process = StoppableHTTPServerProcess(("", port), RestHTTPRequestHandler)
    return process

这是调用代码:

p = start_simple_server(port)
p.start()

当我完成的时候...

p.shutdown()
1

在Python中,atexit模块的功能是当程序正常结束时,自动执行一些清理工作,比如关闭文件或释放资源。但是,如果你强行终止一个进程,比如使用任务管理器结束它,atexit就不会执行了。这意味着你可能会错过一些重要的清理步骤。

>>>import atexit
>>> def hook():
...   print "hook ran"
... 
>>> atexit.register(hook)
<function hook at 0x100414aa0>
>>> 
# in another terminal: kill <python process id>
>>> Terminated

撰写回答