Как остановить приложение 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.