如何在不使用 ctrl-c 的情况下停止烧瓶应用程序

Posted

技术标签:

【中文标题】如何在不使用 ctrl-c 的情况下停止烧瓶应用程序【英文标题】:How to stop flask application without using ctrl-c 【发布时间】:2013-03-11 20:14:42 【问题描述】:

我想实现一个可以通过使用flask-script 来停止flask 应用程序的命令。 我已经搜索了一段时间的解决方案。因为该框架不提供app.stop() API,我很好奇如何编写这个代码。我正在开发 Ubuntu 12.10 和 Python 2.7.3。

【问题讨论】:

为什么您需要能够从脚本中停止您的应用程序? (这项工作的最佳工具取决于您要做什么)。 说真的,你想在这里做什么?如果您正在谈论用于开发的 devserver,那么像这样停止它是完全可以的。在生产中,您不会像这样部署,您可以随时停止请求,因此“应用程序停止运行”。 @SeanVieira 我想知道是否有任何解决方案可以做到这一点。 @IgnasB。我现在正在我的机器上开发一个 RESTful 服务。我正在做一个项目,也许它会帮助我选择我应该部署哪些机器。我能弄清楚的唯一方法是通过终止进程来关闭。 @vrootic,但无论如何您都不会在生产中使用 app.run()。 app.run() 仅用于开发和在开发时测试您的应用程序。在生产中运行 Flask 有不同的方法,更多可以在这里找到,例如 flask.pocoo.org/docs/quickstart/#deploying-to-a-web-server 如果你已经以某种方式部署(所以我误解了问题),停止向 Flask 提供请求的方法是停止 http 服务器这是服务它。 【参考方案1】:

如果你只是在桌面上运行服务器,你可以暴露一个端点来终止服务器(阅读更多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.get('/shutdown')
def shutdown():
    shutdown_server()
    return 'Server shutting down...'
    

这是另一种更包含的方法:

from multiprocessing import Process

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

如果这有帮助,请告诉我。

【讨论】:

您知道是否有任何方法可以在不需要请求上下文的情况下获取“werkzeug.server.shutdown”属性? 我必须将路由方法更改为“GET”才能使其正常工作。 为了完整起见,此答案缺少您将在请求上下文之外调用以执行关闭的函数,这只不过是对服务器的 HTTP 请求(可以来自/发往 localhost) 使用methods='POST',我收到405 Method not allowed 错误,而使用methods='GET'`,它按照@CS 的建议工作。 在 AWS Elastic Beanstalk 上对我不起作用。在当地运作良好【参考方案2】:

我使用线程做的略有不同

from werkzeug.serving import make_server

class ServerThread(threading.Thread):

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

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

    def shutdown(self):
        self.server.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请求库发送请求。

【讨论】:

我没有设法让其他东西工作,但这个解决方案效果很好!万分感谢!对于其他人:它也适用于flask restful! 这似乎在 Windows 上被阻止,直到我用另一个请求点击它......有什么办法吗? 我和@Claudiu 有同样的问题,除了在 Linux 上使用 python 3.6.2 我不知道为什么这不被接受,但它似乎是最干净的,并且在没有任何额外依赖的情况下工作得很好。非常感谢。 在这种情况下,您将端点放在哪里?【参考方案3】:

这是一个有点老的线程,但是如果有人尝试、学习或测试基本的烧瓶应用程序,从后台运行的脚本开始,停止它的最快方法是终止在你所在端口上运行的进程运行您的应用程序。 注意:我知道作者正在寻找一种不杀死或停止应用程序的方法。但这可能对正在学习的人有所帮助。

sudo netstat -tulnp | grep :5001

你会得到这样的东西。

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

要停止应用程序,请终止进程

sudo kill 28834

【讨论】:

我必须使用 sudo kill -9 28834 才能杀死进程。【参考方案4】:

我的方法可以通过 bash 终端/控制台进行

1) 运行并获取进程号

$ ps aux | grep yourAppKeywords

2a) 终止进程

$ kill processNum

2b) 如果上述方法不起作用,则终止进程

$ kill -9 processNum

【讨论】:

我几乎可以肯定问题不是“如何杀死进程”,问题是执行 ctrl+c 不会杀死它。顺便说一句,我确实使用 kill -9 `lsof -i:5000 -t` 因为只有 1 个应用程序可以使用该端口并且很容易。【参考方案5】:

正如其他人指出的那样,您只能在请求处理程序中使用werkzeug.server.shutdown。我发现在其他时间关闭服务器的唯一方法是向自己发送请求。例如,这个 sn-p 中的 /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.post('/seriouslykill')
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.post('/kill')
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..."

【讨论】:

这可行,但感觉... 非常 hacky。我知道这已经有一段时间了,但你有没有找到一种干净的方法来做到这一点,而无需向自己发送请求?【参考方案6】:

这是一个老问题,但谷歌搜索并没有让我知道如何完成这个问题。

因为我没有正确阅读code here! (呸!) 它的作用是在request.environ 中没有werkzeug.server.shutdown 时引发RuntimeError...

所以在没有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
...

无需向自己发送请求。

【讨论】:

【参考方案7】:

如果您正在使用 CLI 并且只有一个烧瓶应用程序/进程正在运行(或者更确切地说,您只想终止系统上运行的 任何烧瓶进程),您可以终止它与:

kill $(pgrep -f flask)

【讨论】:

【参考方案8】:

如果您不在请求-响应处理范围内,您仍然可以:

import os
import signal

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

【讨论】:

很好,谢谢。我需要在 Ctrl+C 终止之前添加一些清理代码,所以我创建了一个 SIGINT 处理程序,它完成这项工作,然后调用您的代码。【参考方案9】:

您不必按 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”。也可以看Execute a function after Flask returns response来解决。 @samm,从这个问题得出的结论是,除非您启动不同的线程,否则这是不可能的,对吧?那么如何从那个不同的线程关闭烧瓶服务器呢?【参考方案10】:

如果其他人正在寻找如何在 win32 服务 中停止 Flask 服务器 - 就在这里。这是几种方法的奇怪组合,但效果很好。关键思想:

    这些是shutdown 端点,可用于正常关机。 注意:它依赖于request.environ.get,它在网络请求的上下文中可用(在@app.route-ed 函数内) win32service 的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://0.0.0.0:5001/shutdown')

【讨论】:

【参考方案11】:

你可以使用下面的方法

app.do_teardown_appcontext()

【讨论】:

这是一个错误的猜测。此函数不会停止应用程序,它会在处理单个请求结束时自动调用以释放一些上下文。 flask.palletsprojects.com/en/1.1.x/api/… 这对我不起作用【参考方案12】:

Google Cloud VM 实例 + Flask 应用

我在 Google Cloud Platform 虚拟机上托管了我的 Flask 应用程序。 我使用python main.py 启动了应用程序,但问题是 ctrl+c 无法停止服务器。

此命令$ sudo netstat -tulnp | grep :5000 终止服务器。

我的 Flask 应用默认在 5000 端口上运行。

注意:我的 VM 实例在 Linux 9 上运行。

它适用于此。其他平台没有测试。 如果它也适用于其他版本,请随时更新或评论。

【讨论】:

请注意:netstat 和 grep 都不会停止活动进程。【参考方案13】:

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 中使用“清除浏览数据 > 图像和文件”。 p>

完成上述所有操作后,我终于在运行 Flask 应用程序时加载了我的网站图标。

【讨论】:

【参考方案14】:

request.environ.getdeprecated。 Pavel Minaev 解决方案很清楚:

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)

【讨论】:

【参考方案15】:

对于 Windows,很容易停止/杀死烧瓶服务器 -

    转到任务管理器 找到flask.exe 选择并结束进程

【讨论】:

你电脑上的电源键也很有效哈哈

以上是关于如何在不使用 ctrl-c 的情况下停止烧瓶应用程序的主要内容,如果未能解决你的问题,请参考以下文章

如何在不停止相机馈送的情况下使用闪光灯?

如何在不停止应用程序的情况下延迟功能

如何在不停止运行应用程序的情况下垂直扩展谷歌云实例

如何在 Ubuntu 上停止 Node.js 并在不停止的情况下注销 [重复]

如何在不使用跟踪句柄的情况下等待未知数量的 Rust 线​​程完成?

如何在不传递引用的情况下在 Python 中使用 SyncManager 跨进程共享列表