Как остановить приложение Flask без использования ctrl-c


108

Я хочу реализовать команду, которая может остановить приложение фляги с помощью скрипта фляги. Некоторое время я искал решение. Поскольку фреймворк не предоставляет API "app.stop ()", мне любопытно, как это кодировать. Я работаю над Ubuntu 12.10 и Python 2.7.3.


Зачем вам нужно иметь возможность останавливать приложение из сценария? (Лучший инструмент для работы будет зависеть от того, что вы пытаетесь сделать).
Шон Виейра,

Серьезно, что ты здесь делаешь? Если вы говорите о devserver для разработки, вполне нормально остановить его вот так. В производственной среде вы не развертываете подобным образом, и вы можете остановить запрос в любое время, когда захотите, поэтому «приложение перестает работать».
Игнас Бутенас

@SeanVieira Я хочу знать, есть ли какие-либо решения для этого.
vic

@IgnasB. Я сейчас разрабатываю службу RESTful на своей машине. Я работаю над проектом, возможно, это поможет мне выбрать, какие машины мне следует развернуть. Единственный способ, которым я могу это понять, - это завершить работу, убив процесс.
vic

3
@vrootic, но вы все равно не будете использовать app.run () в продакшене. app.run () используется только для разработки и для тестирования вашего приложения во время разработки. Есть разные способы запустить Flask в производственной среде, здесь можно найти больше, например, flask.pocoo.org/docs/quickstart/#deploying-to-a-web-server И если вы как-то так уже развернули (так что я неправильно понял вопрос), способ прекратить обслуживание запроса, поступающего во Flask, - это остановить обслуживающий его http-сервер.
Игнас Бутенас

Ответы:


128

Если вы просто запускаете сервер на своем рабочем столе, вы можете открыть конечную точку, чтобы убить сервер (подробнее см. 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...'

Вот еще один более сдержанный подход:

from multiprocessing import Process

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

Позвольте мне знать, если это помогает.


18
Знаете ли вы, есть ли способ получить свойство werkzeug.server.shutdown без контекста запроса?
Акаткинсон

5
Мне пришлось изменить метод маршрута на «GET», чтобы заставить его работать.
CS

5
Для полноты в этом ответе отсутствует функция, которую вы вызывали бы вне контекста запроса для завершения работы, что было бы не чем иным, как HTTP-запросом к серверу (который может исходить с / на localhost)
Джеймс Хатчисон,

1
С methods='POST'я получаю сообщение 405 Method not allowedоб ошибке, тогда как с методами = 'GET' 'он работает так, как предлагает @CS.
Agile Bean

1
Не работал у меня на AWS Elastic Beanstalk. Хорошо
поработал

35

Я сделал это немного иначе, используя потоки

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()

Я использую его для выполнения сквозных тестов для спокойного api, где я могу отправлять запросы, используя библиотеку запросов python.


2
Мне не удалось заставить другие вещи работать, но это решение отлично работает! Благодаря тонну! Для других: он также работает с успокаивающим флаконом!
Йоррик Слейстер,

Кажется, это блокирует окна, пока я не нажму его другим запросом ...
Claudiu

У меня та же проблема, что и у @Claudiu, за исключением Linux с python 3.6.2
micahscopes

Я не знаю, почему это не принято, но кажется, что он самый чистый и отлично работает без каких-либо дополнительных зависимостей. Спасибо.
Эрик Рид,

21

Это немного старый поток, но если кто-то экспериментирует, изучает или тестирует базовое приложение фляги, запущенное из скрипта, который работает в фоновом режиме, самый быстрый способ остановить его - убить процесс, запущенный на порту, на котором запущено ваше приложение. на. Примечание: я знаю, что автор ищет способ не убивать и не останавливать приложение. Но это может помочь тому, кто учится.

sudo netstat -tulnp | grep :5001

Вы получите что-то вроде этого.

tcp 0 0 0.0.0.0:5001 0.0.0.0:* СЛУШАТЬ 28834 / python

Чтобы остановить приложение, завершите процесс

sudo kill 28834

Я должен был использовать, sudo kill -9 28834прежде чем процесс можно было убить.
Удо Э.

17

Мой метод можно продолжить через терминал / консоль bash

1) запустить и получить номер процесса

$ ps aux | grep yourAppKeywords

2а) убить процесс

$ kill processNum

2b) убить процесс, если выше не работает

$ kill -9 processNum

10
Я почти уверен, что вопрос не в том, «как убить процесс», а проблема в том, что нажатие ctrl + c не убивает его. Кстати, я действительно использую, kill -9 `lsof -i:5000 -t`потому что только одно приложение может использовать порт, и это легко.
m3nda

10

Как указывали другие, вы можете использовать только werkzeug.server.shutdownиз обработчика запросов. Я нашел единственный способ выключить сервер в другое время - это послать запрос самому себе. Например, /killобработчик в этом фрагменте завершит работу сервера разработки, если в течение следующей секунды не поступит другой запрос:

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
это работает, но кажется ... очень хакерским. Я знаю, что это было давно, но находили ли вы когда-нибудь чистый способ сделать это, не отправляя запрос самому себе?
Juicy

7

Это старый вопрос, но поиск в Google не дал мне никакого представления о том, как это сделать.

Потому что я неправильно прочитал здесь код ! (Дох!) Что он делает, так это поднимает, RuntimeErrorкогда нет werkzeug.server.shutdownв request.environ...

Итак, что мы можем сделать, когда нет, так requestэто поднятьRuntimeError

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

и поймайте это, когда app.run()вернется:

...
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
...

Не нужно отправлять себе запрос.


4

Вам не нужно нажимать «CTRL-C», но вы можете указать конечную точку, которая сделает это за вас:

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..." })

Теперь вы можете просто вызвать эту конечную точку, чтобы корректно завершить работу сервера:

curl localhost:5000/stopServer

Я проверил ваш код, но после os.killэтого возвращенный ответ не может быть получен клиентом. Для curlон выводит «curl: (56) Recv failure: Connection was reset». См. Также Выполнение функции после того, как Flask вернет ответ для ее решения.
samm

@samm, вывод из этого вопроса - это невозможно, если вы не начнете другую ветку, верно? Тогда как вы выключите сервер фляги из этого другого потока?
Jurgy

3

Вы можете использовать метод ниже

app.do_teardown_appcontext()

2

Если вы работаете с CLI и у вас работает только одно приложение / процесс фляги (или, скорее, вы просто хотите убить любой процесс фляги, запущенный в вашей системе), вы можете убить его с помощью:

kill $(pgrep -f flask)



1

Если кто-то еще ищет, как остановить сервер Flask внутри службы win32 - вот он. Это довольно странное сочетание нескольких подходов, но оно хорошо работает. Ключевые идеи:

  1. Это shutdownконечная точка, которую можно использовать для постепенного завершения работы. Примечание : он полагается на то, request.environ.getчто можно использовать только в контексте веб-запроса ( @app.routeфункция внутри -ed)
  2. Метод win32service SvcStopиспользует requestsHTTP-запрос к самой службе.

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

Экземпляр виртуальной машины Google Cloud + приложение Flask

Я разместил свое приложение Flask на виртуальной машине Google Cloud Platform. Я запустил приложение, используя python main.pyНо проблема была в том, что ctrl + c не помогло остановить сервер.

Эта команда $ sudo netstat -tulnp | grep :5000завершает работу сервера.

Приложение My Flask по умолчанию работает на порту 5000.

Примечание. Мой экземпляр виртуальной машины работает в Linux 9.

Это работает для этого. На других платформах не тестировался. Не стесняйтесь обновлять или комментировать, если это работает и для других версий.


0

Решение Python

Запуск с: python kill_server.py.

Это только для Windows . Убивает серверы с помощью taskkill, по PID, собранному с помощью netstat.

# kill_server.py

import os
import subprocess
import re

port = 5000
host = '127.0.0.1'
cmd_newlines = r'\r\n'

host_port = host + ':' + str(port)
pid_regex = re.compile(r'[0-9]+$')

netstat = subprocess.run(['netstat', '-n', '-a', '-o'], stdout=subprocess.PIPE)  
# Doesn't return correct PID info without precisely these flags
netstat = str(netstat)
lines = netstat.split(cmd_newlines)

for line in lines:
    if host_port in line:
        pid = pid_regex.findall(line)
        if pid:
            pid = pid[0]
            os.system('taskkill /F /PID ' + str(pid))
        
# And finally delete the .pyc cache
os.system('del /S *.pyc')

Если у вас возникли проблемы с загрузкой значка / изменений в index.html (т. Е. Старые версии кешируются), попробуйте также «Очистить данные просмотра> Изображения и файлы» в Chrome .

Выполнив все вышеперечисленное, я получил свой значок, который наконец загрузился после запуска моего приложения Flask.


-3

Для Windows довольно легко остановить / убить флеш-сервер -

  1. Перейти к диспетчеру задач
  2. Найдите flask.exe
  3. Выбрать и завершить процесс

13
Кнопка питания на вашем компьютере тоже довольно эффективна, ха-ха,
Майкл Грин,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.