29

スレッドまたはサブプロセスなしでボトル Web サーバーを起動する場合、問題はありません。ボトルアプリを終了するには -> CTRL+ c.

スレッドで、ボトル Web サーバーをプログラムで停止するにはどうすればよいですか?

stop()ドキュメントでメソッドなどを見つけられませんでした。理由はありますか?

4

12 に答える 12

15

デフォルト(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()

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

@app.route('/stop')  # not working from here, it has to come from another thread
def stopit():
    server.stop()  

server = MyWSGIRefServer(port=80)
try:
    app.run(server=server)
except:
    print('Bye')

別のスレッドからボトル アプリケーションを停止する場合は、次のようにします。

server.stop()
于 2013-04-17T09:30:48.770 に答える
10

ボトルはサブプロセスでリクエストを実行しているように見えるため、リクエスト内からボトルサーバーを閉じるのに問題がありました。

最終的に、解決策は次のことであることがわかりました。

sys.stderr.close()

リクエスト内(ボトルサーバーに渡され、それを斧した)。

于 2012-07-15T04:28:21.730 に答える
8

マイクの回答の更新版。

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() メソッドに 1 行だけ追加されます。また、単純な shutdown() メソッドを追加します。残念ながら、これはボトルが run() メソッドを作成する方法のために必要です。

于 2013-11-03T04:51:32.683 に答える
3

startを呼び出す前にdaemonプロパティをTrueに設定することで、スレッドをデーモンにすることができます。

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

デーモンスレッドは、それが実行されているメインスレッドが強制終了または終了するたびに停止します。唯一の問題は、終了時にスレッドにコードを実行させることができず、スレッドが何かを実行している場合、実行中のメソッドを終了できずにすぐに停止することです。

Pythonには、実際にスレッドを明示的に停止する方法はありません。サーバーを停止できるように制御したい場合は、 multiprocessesモジュールからPythonプロセスを調べる必要があります。

于 2012-08-20T14:56:16.823 に答える
2

ここに 1 つのオプションがあります: それ自体を記録するカスタム サーバー (デフォルトと同じ) を提供します。

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)
于 2015-11-20T15:07:34.140 に答える
2

bottle はメカニズムを提供しないため、ハックが必要です。デフォルトの 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
        ...
于 2013-02-22T06:49:01.587 に答える
1

ボトル Web サーバーは、終了するまで永久に実行されると思います。のようなメソッドはありません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) if not callable(app): raise ValueError("Application is not callable: %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ん。

于 2012-07-01T13:30:56.963 に答える
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()
于 2014-03-02T00:42:34.743 に答える