Рамка для бутылок - как остановить?
При запуске веб-сервера с бутылкой без потока или подпроцесса нет проблем. Чтобы выйти из приложения для бутылки → CTRL
+ c
.
В потоке, как я могу программно остановить веб-сервер бутылки?
Я не нашел метод stop()
или что-то подобное в документации. Есть ли причина?
Ответы
Ответ 1
Для сервера по умолчанию (WSGIRef) это то, что я делаю (на самом деле это более чистый подход предложения Викрама Пуди):
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()
Ответ 2
У меня возникла проблема с закрытием сервера бутылок из запроса, так как бутылка, похоже, запускает запросы в подпроцессах.
В итоге я нашел решение:
sys.stderr.close()
внутри запроса (который прошел до сервера бутылки и опустил его).
Ответ 3
Обновленная версия ответа на майк.
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(). К сожалению, это необходимо из-за того, как бутылка создает метод run().
Ответ 4
Здесь один из вариантов: предоставить настраиваемый сервер (тот же, что и по умолчанию), который записывает себя:
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)
Ответ 5
Я полагаю, что веб-сервер бутылки работает вечно до тех пор, пока он не прекратится. Нет таких метанолов, как 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 isststance (app, basestring): app = load_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
в любом случае не существует.
Ответ 6
Вы можете сделать свой поток демонами, установив для свойства daemon значение True перед вызовом start.
mythread = threading.Thread()
mythread.daemon = True
mythread.start()
Нить деамонов останавливается всякий раз, когда основной поток, в котором он работает, убивается или умирает. Единственная проблема заключается в том, что вы не сможете заставить поток запускать какой-либо код при выходе, и если поток находится в процессе выполнения чего-либо, он будет немедленно остановлен без возможности завершить метод, в котором он запущен.
В Python нет возможности фактически явно остановить поток. Если вы хотите получить больше контроля над возможностью остановить свой сервер, вы должны изучить Python Процессы из многопроцессы.
Ответ 7
Поскольку бутылка не обеспечивает механизм, она требует взлома. Это, пожалуй, самый чистый, если вы используете сервер 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
...
Ответ 8
У этого одинаково худшего хака есть то преимущество, что у вас нет копии кода в файле 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()
Ответ 9
Этот вопрос был главным в моем поиске в 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 остановит сервер.