如何停止烧瓶应用不使用 ctrl-c

我想实现一个命令,可以停止烧瓶应用使用烧瓶脚本。 我已经找到解决办法有一段时间了。因为这个框架没有提供 app.stop() API,所以我很好奇如何编写这个代码。我正在编写 Ubuntu 12.10和 Python 2.7.3。

226978 次浏览

如果您只是在桌面上运行服务器,您可以公开一个端点来终止服务器(详见 关闭简单服务器) :

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。我发现在另一个时间关闭服务器的唯一方法是向您自己发送一个请求。例如,此代码片段中的 /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..."

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

1)运行并获取进程编号

$ ps aux | grep yourAppKeywords

2a)终止进程

$ kill processNum

2b)如果上述过程不起作用,则终止该过程

$ kill -9 processNum

这是一个古老的问题,但谷歌并没有给我任何关于如何实现这一点的见解。

因为我没有正确阅读 这里有密码! (啊!) 它的作用是在 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
...

没必要向自己发送请求。

我用的线稍微有点不同

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')
# App routes defined here
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:50010.0.0: * LISTEN 28834/python

要停止应用程序,请关闭进程

sudo kill 28834

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

  1. Goto 任务管理器
  2. 找到 Flask.exe
  3. 选择和结束进程

你可以使用下面的方法

app.do_teardown_appcontext()

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

kill $(pgrep -f flask)

你不必按 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

如果处于请求-响应处理之外,您仍然可以:

import os
import signal


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

Google 云虚拟机实例 + Flask 应用程序

我在谷歌云平台虚拟机上托管了我的烧瓶应用程序。 我使用 python main.py启动了这个应用程序,但问题是 ctrl + c 无法停止服务器。

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

默认情况下,我的 Flask 应用程序在端口5000上运行。

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

这个很管用,还没有在其他平台上测试过。 如果其他版本也适用,请随时更新或评论。

如果有人正在寻找如何停止内部 Win32服务烧瓶服务器-在这里。这是几种方法的奇怪组合,但是它工作得很好。关键理念:

  1. 这些是 shutdown端点,可用于优雅的关机。注意: 它依赖于在 Web 请求的上下文(在 @app.route-ed 函数内)中可用的 只有
  2. 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)

我的服务

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

Python 解决方案

运行: python kill_server.py

这是针对 只有窗户的。使用 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 应用程序。

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