瓶网框架 - 如何停止?

时间:2012-07-01 13:08:00

标签: python bottle

在没有线程或子进程的情况下启动瓶子网络服务器时,没有问题。退出瓶子应用程序 - > CTRL + c

在一个帖子中,我如何以编程方式停止瓶子网络服务器?

我没有在文档中找到stop()方法或类似的方法。有原因吗?

11 个答案:

答案 0 :(得分:12)

对于默认(WSGIRef)服务器,这就是我所做的(实际上它是Vikram Pudi建议的一种更清晰的方法):

from bottle import Bottle, ServerAdapter

class MyWSGIRefServer(ServerAdapter):
    server = None

    def run(self, handler):
        from wsgiref.simple_server import make_server, WSGIRequestHandler
        if self.quiet:
            class QuietHandler(WSGIRequestHandler):
                def log_request(*args, **kw): pass
            self.options['handler_class'] = QuietHandler
        self.server = make_server(self.host, self.port, handler, **self.options)
        self.server.serve_forever()

    def stop(self):
        # self.server.server_close() <--- alternative but causes bad fd exception
        self.server.shutdown()

app = Bottle()
server = MyWSGIRefServer(host=listen_addr, port=listen_port)
try:
    app.run(server=server)
except Exception,ex:
    print ex

当我想从另一个线程停止瓶子应用程序时,我会执行以下操作:

server.stop()

答案 1 :(得分:7)

我无法在请求中关闭瓶子服务器,因为瓶子似乎在子流程中运行请求。

我最终找到了解决办法:

sys.stderr.close()

在请求中(已经传递给瓶子服务器并将其砍掉)。

答案 2 :(得分:5)

迈克回答的更新版本。

from bottlepy.bottle import WSGIRefServer, run
from threading import Thread
import time

class MyServer(WSGIRefServer):
    def run(self, app): # pragma: no cover
        from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
        from wsgiref.simple_server import make_server
        import socket

        class FixedHandler(WSGIRequestHandler):
            def address_string(self): # Prevent reverse DNS lookups please.
                return self.client_address[0]
            def log_request(*args, **kw):
                if not self.quiet:
                    return WSGIRequestHandler.log_request(*args, **kw)

        handler_cls = self.options.get('handler_class', FixedHandler)
        server_cls  = self.options.get('server_class', WSGIServer)

        if ':' in self.host: # Fix wsgiref for IPv6 addresses.
            if getattr(server_cls, 'address_family') == socket.AF_INET:
                class server_cls(server_cls):
                    address_family = socket.AF_INET6

        srv = make_server(self.host, self.port, app, server_cls, handler_cls)
        self.srv = srv ### THIS IS THE ONLY CHANGE TO THE ORIGINAL CLASS METHOD!
        srv.serve_forever()

    def shutdown(self): ### ADD SHUTDOWN METHOD.
        self.srv.shutdown()
        # self.server.server_close()

def begin():
    run(server=server)

server = MyServer(host="localhost", port=8088)
Thread(target=begin).start()
time.sleep(2) # Shut down server after 2 seconds
server.shutdown()

类WSGIRefServer完全复制,只添加了一行添加到run()方法中。还添加一个简单的shutdown()方法。不幸的是,由于bottle创建run()方法的方式,这是必要的。

答案 3 :(得分:3)

这里有一个选项:提供自定义服务器(与默认服务器相同),记录自己:

import bottle


class WSGI(bottle.WSGIRefServer):
    instances = []

    def run(self, *args, **kw):
        self.instances.append(self)
        super(WSGI, self).run(*args, **kw)

# some other thread:
bottle.run(host=ip_address, port=12345, server=WSGI)

# control thread:
logging.warn("servers are %s", WSGI.instances)

答案 4 :(得分:1)

我认为瓶子网络服务器会一直运行直到它终止。没有像stop()这样的方法。

但你可以做这样的事情:

from bottle import route, run
import threading, time, os, signal, sys, operator

class MyThread(threading.Thread):
    def __init__(self, target, *args):
        threading.Thread.__init__(self, target=target, args=args)
        self.start()

class Watcher:
    def __init__(self):
        self.child = os.fork()
        if self.child == 0:
            return
        else:
            self.watch()

    def watch(self):
        try:
            os.wait()
        except KeyboardInterrupt:
            print 'KeyBoardInterrupt'
            self.kill()
        sys.exit()

    def kill(self):
        try:
            os.kill(self.child, signal.SIGKILL)
        except OSError: pass

def background_process():
    while 1:
        print('background thread running')
        time.sleep(1)

@route('/hello/:name')
def index(name='World'):
    return '<b>Hello %s!</b>' % name

def main():
    Watcher()
    MyThread(background_process)

    run(host='localhost', port=8080)

if __name__ == "__main__":
    main()

然后,当您需要杀死服务器时,可以使用Watcher.kill()

以下是瓶子run()功能的代码:

尝试:         app = app或default_app()         if isinstance(app,basestring):             app = load_app(app)         如果不可调用(app):             提高ValueError(“应用程序不可调用:%r”%app)

    for plugin in plugins or []:
        app.install(plugin)

    if server in server_names:
        server = server_names.get(server)
    if isinstance(server, basestring):
        server = load(server)
    if isinstance(server, type):
        server = server(host=host, port=port, **kargs)
    if not isinstance(server, ServerAdapter):
        raise ValueError("Unknown or unsupported server: %r" % server)

    server.quiet = server.quiet or quiet
    if not server.quiet:
        stderr("Bottle server starting up (using %s)...\n" % repr(server))
        stderr("Listening on http://%s:%d/\n" % (server.host, server.port))
        stderr("Hit Ctrl-C to quit.\n\n")

    if reloader:
        lockfile = os.environ.get('BOTTLE_LOCKFILE')
        bgcheck = FileCheckerThread(lockfile, interval)
        with bgcheck:
            server.run(app)
        if bgcheck.status == 'reload':
            sys.exit(3)
    else:
        server.run(app)
except KeyboardInterrupt:
    pass
except (SyntaxError, ImportError):
    if not reloader: raise
    if not getattr(server, 'quiet', False): print_exc()
    sys.exit(3)
finally:
    if not getattr(server, 'quiet', False): stderr('Shutdown...\n')

正如您所看到的,除了一些例外情况,除了run循环之外没有其他方法可以取消。 server.run函数取决于您使用的服务器,但无论如何都没有通用quit - 方法。

答案 5 :(得分:1)

通过在调用start之前将守护进程属性设置为True,可以使线程成为守护进程。

mythread = threading.Thread()
mythread.daemon = True
mythread.start()

守护线程将在其运行的主线程被终止或死亡时停止。唯一的问题是你不能让线程在退出时运行任何代码,如果线程正在做某事,它将立即停止,而不能完成它正在运行的方法。

Python中没有办法实际明确地停止线程。如果您希望能够更好地控制能够停止服务器,那么您应该从Processes模块中查看Python multiprocesses

答案 6 :(得分:1)

由于瓶子不提供机制,因此需要黑客攻击。如果您使用默认的WSGI服务器,这可能是最干净的一个:

在瓶子的代码中,WSGI服务器以:

启动
srv.serve_forever()

如果您已在自己的线程中启动了瓶子,则可以使用以下命令停止:

srv.shutdown()

要访问代码中的srv变量,您需要编辑瓶源代码并使其成为全局变量。更改瓶代码后,它看起来像:

srv = None #make srv global
class WSGIRefServer(ServerAdapter):
    def run(self, handler): # pragma: no cover
        global srv #make srv global
        ...

答案 7 :(得分:1)

这个同样重要的黑客攻击的优点是没有你从bottle.py中复制粘贴任何代码:

# The global server instance.                                                                                             
server = None

def setup_monkey_patch_for_server_shutdown():
    """Setup globals to steal access to the server reference.                                                             
    This is required to initiate shutdown, unfortunately.                                                                 
    (Bottle could easily remedy that.)"""

    # Save the original function.                                                                                         
    from wsgiref.simple_server import make_server

    # Create a decorator that will save the server upon start.                                                            
    def stealing_make_server(*args, **kw):
        global server
        server = make_server(*args, **kw)
        return server

    # Patch up wsgiref itself with the decorated function.                                                                
    import wsgiref.simple_server
    wsgiref.simple_server.make_server = stealing_make_server

setup_monkey_patch_for_server_shutdown()

def shutdown():
    """Request for the server to shutdown."""
    server.shutdown()

答案 8 :(得分:1)

这与seperomike的答案是完全相同的方法,但是现在使用Bottle版本0.13+更简单了:

from bottle import W, run, route
from threading import Thread
import time

@route('/')
def index():
    return 'Hello world'

def shutdown():
    time.sleep(5)
    server.srv.shutdown()

server = WSGIRefServer(port=80)
Thread(target=shutdown).start()
run(server=server)

也相关:https://github.com/bottlepy/bottle/issues/1229https://github.com/bottlepy/bottle/issues/1230


另一个使用路由http://localhost/stop进行关闭的示例:

from bottle import WSGIRefServer, run, route
from threading import Thread

@route('/')
def index():
    return 'Hello world'

@route('/stop')
def stopit():
    Thread(target=shutdown).start()

def shutdown():
    server.srv.shutdown()

server = WSGIRefServer(port=80)
run(server=server)

PS:至少需要Bottle 0.13dev。

答案 9 :(得分:0)

这个问题在我的Google搜索中排在首位,所以我将发布答案:

使用Bottle()类启动服务器时,它具有方法close()来停止服务器。从源代码:

  

“”“关闭应用程序和所有已安装的插件。”“”

例如:

class Server:
    def __init__(self, host, port):
        self._host = host
        self._port = port
        self._app = Bottle()
    def stop(self):
        # close ws server
        self._app.close()
    def foo(self):
        # More methods, routes...

调用stop方法将停止服务器。

答案 10 :(得分:0)

我发现此解决方案是最简单的,但确实需要安装“ psutil”软件包才能获取当前进程。它还需要“信号”模块,但这是标准库的一部分。

@route('/shutdown')
def shutdown():
    current_process = psutil.Process()
    current_process.send_signal(signal.CTRL_C_EVENT)
    return 'Shutting down the web server'

希望对某人有用!