Jak zatrzymać aplikację kolby bez użycia ctrl-c


107

Chcę zaimplementować polecenie, które może zatrzymać aplikację kolby za pomocą skryptu flask. Przez chwilę szukałem rozwiązania. Ponieważ framework nie zapewnia API "app.stop ()", jestem ciekawy, jak to zakodować. Pracuję na Ubuntu 12.10 i Python 2.7.3.


Dlaczego musisz mieć możliwość zatrzymania aplikacji ze skryptu? (Najlepsze narzędzie do pracy będzie zależeć od tego, co próbujesz zrobić).
Sean Vieira

Poważnie, co próbujesz tutaj zrobić? Jeśli mówisz o devserver do programowania, możesz to tak zatrzymać. W środowisku produkcyjnym nie wdrażasz w ten sposób i możesz zatrzymać żądanie w dowolnym momencie, więc „aplikacja przestaje działać”.
Ignas Butėnas

@SeanVieira Chcę wiedzieć, czy są jakieś rozwiązania, aby to zrobić.
zwycięstwo

@IgnasB. W tej chwili tworzę usługę RESTful na moim komputerze. Pracuję nad projektem, być może pomoże mi to w wyborze maszyn, które powinienem wdrożyć. Jedynym sposobem, w jaki mogę to rozgryźć, jest zamknięcie procesu przez zabicie procesu.
zwycięstwo

3
@vrootic, ale i tak nie będziesz używać app.run () w produkcji. app.run () służy tylko do programowania i testowania aplikacji podczas programowania. Istnieją różne sposoby na uruchomienie Flaska w środowisku produkcyjnym, więcej można znaleźć tutaj, na przykład flask.pocoo.org/docs/quickstart/#deploying-to-a-web-server A jeśli już w jakiś sposób wdrożysz taką aplikację (więc źle zrozumiałem pytanie), sposobem na zatrzymanie obsługi żądania przychodzącego do Flaska jest zatrzymanie serwera http, który je obsługuje.
Ignas Butėnas

Odpowiedzi:


127

Jeśli po prostu uruchamiasz serwer na swoim pulpicie, możesz ujawnić punkt końcowy, aby zabić serwer (przeczytaj więcej w Shutdown The Simple Server ):

from flask import request
def shutdown_server():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()

@app.route('/shutdown', methods=['POST'])
def shutdown():
    shutdown_server()
    return 'Server shutting down...'

Oto inne podejście, które jest bardziej ograniczone:

from multiprocessing import Process

server = Process(target=app.run)
server.start()
# ...
server.terminate()
server.join()

Daj mi znać, jeśli to pomoże.


17
Czy wiesz, czy istnieje sposób na uzyskanie właściwości „werkzeug.server.shutdown” bez potrzeby kontekstu żądania?
akatkinson

5
Musiałem zmienić metodę trasy na „GET”, aby działała.
CS

5
Dla kompletności w tej odpowiedzi brakuje funkcji, którą można by wywołać poza kontekstem żądania, aby wykonać zamknięcie, co byłoby niczym innym jak żądaniem HTTP do serwera (które może pochodzić z / do localhost)
JamesHutchison

1
W przypadku methods='POST'otrzymuję 405 Method not allowedbłąd, podczas gdy z metodami = 'GET' 'działa tak, jak sugerował @CS.
Agile Bean

1
Nie działa dla mnie na AWS Elastic Beanstalk. Działał dobrze lokalnie
Andrey Bulezyuk

34

Zrobiłem to trochę inaczej używając wątków

from werkzeug.serving import make_server

class ServerThread(threading.Thread):

    def __init__(self, app):
        threading.Thread.__init__(self)
        self.srv = make_server('127.0.0.1', 5000, app)
        self.ctx = app.app_context()
        self.ctx.push()

    def run(self):
        log.info('starting server')
        self.srv.serve_forever()

    def shutdown(self):
        self.srv.shutdown()

def start_server():
    global server
    app = flask.Flask('myapp')
    ...
    server = ServerThread(app)
    server.start()
    log.info('server started')

def stop_server():
    global server
    server.shutdown()

Używam go do wykonywania testów od końca do końca dla restful api, gdzie mogę wysyłać żądania za pomocą biblioteki żądań Pythona.


2
Nie udało mi się zmusić innych rzeczy do pracy, ale to rozwiązanie działa świetnie! Wielkie dzięki! Dla innych osób: działa również z butelką uspokajającą!
Jorrick Sleijster

Wydaje się, że blokuje się w systemie Windows, dopóki nie trafię do niego z kolejnym żądaniem ...
Claudiu,

Mam ten sam problem co @Claudiu, z wyjątkiem Linuksa z
Pythonem

Nie wiem, dlaczego nie jest to akceptowane, ale wydaje się, że jest najczystsze i działa świetnie bez żadnych dodatkowych zależności. Dzięki wielkie.
Eric Reed

20

To trochę stary wątek, ale jeśli ktoś eksperymentuje, uczy się lub testuje podstawową aplikację flask, rozpoczął od skryptu działającego w tle, najszybszym sposobem na zatrzymanie go jest zabicie procesu działającego na porcie, na którym uruchamiasz aplikację na. Uwaga: zdaję sobie sprawę, że autor szuka sposobu, aby nie zabijać ani nie zatrzymywać aplikacji. Ale to może pomóc komuś, kto się uczy.

sudo netstat -tulnp | grep :5001

Dostaniesz coś takiego.

tcp 0 0 0.0.0.0:5001 0.0.0.0:* SŁUCHAJ 28834 / python

Aby zatrzymać aplikację, zakończ proces

sudo kill 28834

Musiałem użyć, sudo kill -9 28834zanim proces został zabity.
Udo E.

17

Moją metodę można kontynuować za pośrednictwem terminala / konsoli bash

1) uruchom i uzyskaj numer procesu

$ ps aux | grep yourAppKeywords

2a) zakończyć proces

$ kill processNum

2b) zabij proces, jeśli powyższe nie działa

$ kill -9 processNum

10
Jestem prawie pewien, że pytanie nie brzmi „jak zabić proces”, a problem polega na tym, że wykonanie ctrl + c go nie zabija. Przy okazji, używam, kill -9 `lsof -i:5000 -t`ponieważ żadna inna aplikacja nie może korzystać z portu i jest to łatwe.
m3nda

10

Jak zauważyli inni, możesz używać tylko werkzeug.server.shutdownod osoby zajmującej się wnioskami. Jedynym sposobem, w jaki znalazłem wyłączenie serwera w innym czasie, jest wysłanie żądania do siebie. Na przykład /killprogram obsługi w tym fragmencie kodu zabije serwer deweloperski, chyba że w ciągu następnej sekundy nadejdzie kolejne żądanie:

import requests
from threading import Timer
from flask import request
import time

LAST_REQUEST_MS = 0
@app.before_request
def update_last_request_ms():
    global LAST_REQUEST_MS
    LAST_REQUEST_MS = time.time() * 1000


@app.route('/seriouslykill', methods=['POST'])
def seriouslykill():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server')
    func()
    return "Shutting down..."


@app.route('/kill', methods=['POST'])
def kill():
    last_ms = LAST_REQUEST_MS
    def shutdown():
        if LAST_REQUEST_MS <= last_ms:  # subsequent requests abort shutdown
            requests.post('http://localhost:5000/seriouslykill')
        else:
            pass

    Timer(1.0, shutdown).start()  # wait 1 second
    return "Shutting down..."

3
to działa, ale wydaje się ... bardzo hakerskie. Wiem, że minęło trochę czasu, ale czy kiedykolwiek znalazłeś na to czysty sposób, bez wysyłania prośby do siebie?
Soczysty

7

To stare pytanie, ale wyszukiwanie w Google nie dało mi wglądu w to, jak to osiągnąć.

Ponieważ nie przeczytałem tutaj poprawnie kodu ! (Doh!) To, co robi, to podbić, RuntimeErrorgdy nie ma werkzeug.server.shutdownw request.environ...

Więc co możemy zrobić, gdy nie ma, requestto podbićRuntimeError

def shutdown():
    raise RuntimeError("Server going down")

i złap to, gdy app.run()wrócisz:

...
try:
    app.run(host="0.0.0.0")
except RuntimeError, msg:
    if str(msg) == "Server going down":
        pass # or whatever you want to do when the server goes down
    else:
        # appropriate handling/logging of other runtime errors
# and so on
...

Nie musisz wysyłać sobie prośby.


4

Nie musisz naciskać „CTRL-C”, ale możesz podać punkt końcowy, który zrobi to za Ciebie:

from flask import Flask, jsonify, request
import json, os, signal

@app.route('/stopServer', methods=['GET'])
def stopServer():
    os.kill(os.getpid(), signal.SIGINT)
    return jsonify({ "success": True, "message": "Server is shutting down..." })

Teraz możesz po prostu wywołać ten punkt końcowy, aby bezpiecznie zamknąć serwer:

curl localhost:5000/stopServer

Przetestowałem Twój kod, ale potem os.killklient nie może odebrać zwróconej odpowiedzi. W przypadku curl, wyświetla komunikat „curl: (56) Recv failure: Connection has reset”. Może zobaczyć także Wykonaj funkcję po tym, jak Flask zwróci odpowiedź, aby ją rozwiązać.
samm

@samm, wniosek z tego pytania jest taki, że nie jest to możliwe, dopóki nie rozpoczniesz innego wątku, prawda? Jak więc zamknąć serwer kolbowy z tego innego wątku?
Jurgia

3

Możesz użyć poniższej metody

app.do_teardown_appcontext()

2

Jeśli pracujesz na CLI i masz uruchomioną tylko jedną aplikację / proces kolby (a raczej chcesz po prostu zabić dowolny proces kolby działający w twoim systemie), możesz go zabić:

kill $(pgrep -f flask)


1

Jeśli nie zajmujesz się obsługą zapytań i odpowiedzi, nadal możesz:

import os
import signal

sig = getattr(signal, "SIGKILL", signal.SIGTERM)
os.kill(os.getpid(), sig)

1

Jeśli ktoś jeszcze szuka sposobu zatrzymania serwera Flask w usłudze win32 - oto on. To trochę dziwne połączenie kilku podejść, ale działa dobrze. Kluczowe pomysły:

  1. To jest shutdownpunkt końcowy, który może służyć do bezpiecznego zamykania. Uwaga : opiera się na tym, request.environ.getktóry jest użyteczny tylko w kontekście żądania internetowego ( @app.routefunkcja wewnątrz-wewnątrz )
  2. SvcStopMetoda win32service używa requestsdo wysyłania żądań HTTP do samej usługi.

myservice_svc.py

import win32service
import win32serviceutil
import win32event
import servicemanager
import time
import traceback
import os

import myservice


class MyServiceSvc(win32serviceutil.ServiceFramework):
    _svc_name_ = "MyServiceSvc"                       # NET START/STOP the service by the following name
    _svc_display_name_ = "Display name"  # this text shows up as the service name in the SCM
    _svc_description_ = "Description" # this text shows up as the description in the SCM

    def __init__(self, args):
        os.chdir(os.path.dirname(myservice.__file__))
        win32serviceutil.ServiceFramework.__init__(self, args)

    def SvcDoRun(self):
        # ... some code skipped
        myservice.start()

    def SvcStop(self):
        """Called when we're being shut down"""
        myservice.stop()
        # tell the SCM we're shutting down
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STOPPED,
                              (self._svc_name_, ''))

if __name__ == '__main__':
    os.chdir(os.path.dirname(myservice.__file__))
    win32serviceutil.HandleCommandLine(MyServiceSvc)

myservice.py

from flask import Flask, request, jsonify

# Workaround - otherwise doesn't work in windows service.
cli = sys.modules['flask.cli']
cli.show_server_banner = lambda *x: None

app = Flask('MyService')

# ... business logic endpoints are skipped.

@app.route("/shutdown", methods=['GET'])
def shutdown():
    shutdown_func = request.environ.get('werkzeug.server.shutdown')
    if shutdown_func is None:
        raise RuntimeError('Not running werkzeug')
    shutdown_func()
    return "Shutting down..."


def start():
    app.run(host='0.0.0.0', threaded=True, port=5001)


def stop():
    import requests
    resp = requests.get('http://localhost:5001/shutdown')

0

Instancja maszyny wirtualnej Google Cloud + aplikacja Flask

Hostowałem moją aplikację Flask na maszynie wirtualnej Google Cloud Platform. Uruchomiłem aplikację za pomocą, python main.pyale problem polegał na tym, że ctrl + c nie działało, aby zatrzymać serwer.

To polecenie $ sudo netstat -tulnp | grep :5000przerywa działanie serwera.

Moja aplikacja Flask działa domyślnie na porcie 5000.

Uwaga: moja maszyna wirtualna działa w systemie Linux 9.

To działa w tym przypadku. Nie testowano na innych platformach. Możesz zaktualizować lub skomentować, jeśli działa to również w innych wersjach.


-3

W systemie Windows zatrzymanie / zabicie serwera flask jest dość łatwe -

  1. Idź do Menedżera zadań
  2. Znajdź flask.exe
  3. Wybierz i zakończ proces

13
Przycisk zasilania na twoim komputerze też jest całkiem skuteczny haha
Michael Green
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.