Как остановить колбу без использования Ctrl-C

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

21 ответ

Решение

Если вы просто запускаете сервер на своем рабочем столе, вы можете предоставить конечную точку для уничтожения сервера (подробнее см. " Завершение работы простого сервера"):

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

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

Я сделал это немного по-другому, используя темы

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

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

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

sudo netstat -tulnp | grep :5001

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

tcp 0 0 0.0.0.0:5001 0.0.0.0:* LISTEN 28834 / python

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

sudo kill 28834

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

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

$ ps aux | grep yourAppKeywords

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

$ kill processNum

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

$ kill -9 processNum

Вам не нужно нажимать "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

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

import requests
from threading import Timer
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..."

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

Потому что я не прочитал код здесь правильно! (Doh!) Что он делает, это поднять 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
...

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

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

kill $(pgrep -f flask)

Если вы находитесь за пределами обработки запроса-ответа, вы все равно можете:

import os
import signal

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

request.environ.get устарело . Решение Павла Минаева довольно ясное:

      import os
from flask import Flask


app = Flask(__name__)
exiting = False

@app.route("/exit")
def exit_app():
    global exiting
    exiting = True
    return "Done"

@app.teardown_request
def teardown(exception):
    if exiting:
        os._exit(0)

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

app.do_teardown_appcontext()

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

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

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

Я обнаружил, что удаление собственного PID работает следующим образом:

      import os
from flask import Flask

app = Flask(__name__)
own_pid = os.getpid() # Get the main process's PID in a global variable

@app.route('/kill-backend')
def kill_backend():
    global own_pid # Make sure to use the global variable
    os.kill(own_pid, 9) # The second argument is the signal, 9 stands for SIGKILL.
#If you want to "politely ask" the server to quit you can use SIGQUIT (15) instead.

app.run(host='0.0.0.0', port=8000)

Я тестировал это на Ubuntu 22.04 LTS и на Window$ 10. Это работает на обоих.

Чтобы вызвать уничтожение сервера, просто запроситеhttp://127.0.0.1/kill-backendи это потерпит неудачу сconnection refusedошибка, потому что сервер сразу мертв.

Если порт известен (например, 5000), я нашел простое решение: введите:

      fuser -k 5000/tcp

это убьет процесс на порту 5000.

Как убить процесс, работающий на определенном порту в Linux?

Решение 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.

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

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

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

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

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

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

вы можете попробовать использовать

      pkill python

pkill — это утилита командной строки, которая отправляет сигналы процессам работающей программы на основе заданных критериев. Процессы могут быть указаны по их полным или частичным именам, по пользователю, запускающему процесс, или по другим атрибутам.

Команда pkill является частью пакета procps (или procps-ng), который предварительно установлен почти во всех дистрибутивах Linux. pkill — это, по сути, оболочка программы pgrep, которая печатает только список соответствующих процессов. Как использовать команду pkill

Синтаксис команды pkill следующий:

      pkill [OPTIONS] <PATTERN>

Соответствие задается с помощью расширенных регулярных выражений.

При вызове без каких-либо опций pkill отправляет сигнал 15 (TERM) PID всех запущенных программ, соответствующих данному имени. Например, чтобы корректно остановить все процессы Firefox, вы должны запустить:

      pkill -15 python

Команда возвращает 0, если хотя бы один запущенный процесс соответствует запрошенному имени. В противном случае код выхода равен 1. Это может быть полезно при написании сценариев оболочки.

больше информации здесь

pkill-команда в Linux

Проблема в том, что werkzeugBaseWSGIServerэкземпляр, созданныйwerkzeug.serving.run_simple()(который называетсяapp.run()) не доступен. Тем не менее, этот класс определяет чистыйshutdown()метод, который эффективно завершает основной цикл сервера без каких-либо хаков.

Итак, если мы сможем получить доступ к этому экземпляру, мы сможем убить сервер. Один из способов, которым я могу это сделать, - это отслеживание объектов сборщика мусора:

      @app.route("/shutdown", methods=["POST"])
def shutdown():
    for obj in gc.get_objects():
        try:
            if isinstance(obj, BaseWSGIServer):
                obj.shutdown()
                return "bye"
        except:
            pass
    return "failed"

Мне это понадобилось для эмуляции обратных вызовов POST в моих модульных тестах. С помощью этой функции я могу запускать сервер flask в потоке и не беспокоиться о том, что он останется работоспособным, пока я выполняю другие тесты.

      app = MyFlaskSubclass()

...

app.httpd = MyWSGIServerSubclass()

...
 
@app.route('/shutdown')
def app_shutdown():
    from threading import Timer
    t = Timer(5, app.httpd.shutdown)
    t.start()
    return "Server shut down"

Мой вариант сценария bash (LINUX):

      #!/bin/bash
portFind="$1"
echo "Finding process on port: $portFind"
pid=$(netstat -tulnp | grep :"$1" | awk '{print $7}' | cut -f1 -d"/")
echo "Process found: $pid"
kill -9 $pid
echo "Process $pid killed"

Пример использования:

      sudo bash killWebServer.sh 2223

Выход:

      Finding process on port: 2223
Process found: 12706
Process 12706 killed

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

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