วิธีหยุดแอปพลิเคชันขวดโดยไม่ใช้ ctrl-c


112

ฉันต้องการใช้คำสั่งที่สามารถหยุดแอปพลิเคชัน flask โดยใช้ flask-script ฉันได้ค้นหาวิธีแก้ปัญหามาระยะหนึ่งแล้ว เนื่องจากเฟรมเวิร์กไม่มี API "app.stop ()" ฉันจึงอยากรู้เกี่ยวกับวิธีการเขียนโค้ด ฉันกำลังทำงานบน Ubuntu 12.10 และ Python 2.7.3


เหตุใดคุณจึงต้องสามารถหยุดแอปพลิเคชันของคุณจากสคริปต์ได้ (เครื่องมือที่ดีที่สุดสำหรับงานจะขึ้นอยู่กับสิ่งที่คุณพยายามทำ)
Sean Vieira

อย่างจริงจังคุณกำลังพยายามทำอะไรที่นี่? หากคุณกำลังพูดถึง devserver สำหรับการพัฒนามันเป็นการดีอย่างยิ่งที่จะหยุดมันไว้แบบนั้น ในการใช้งานจริงคุณจะไม่ปรับใช้เช่นนี้และคุณสามารถหยุดคำขอได้ทุกเมื่อที่ต้องการดังนั้น "แอปจึงหยุดทำงาน"
Ignas Butėnas

@SeanVieira ฉันอยากรู้ว่ามีวิธีแก้ปัญหานี้ไหม
vic

@IgnasB. ฉันกำลังพัฒนาบริการ RESTful บนเครื่องของฉันตอนนี้ ฉันกำลังทำโปรเจ็กต์อยู่บางทีมันอาจจะช่วยฉันเลือกเครื่องที่ฉันควรจะติดตั้งวิธีเดียวที่ฉันจะรู้ได้คือการปิดระบบโดยการฆ่ากระบวนการ
vic

3
@vrootic แต่คุณจะไม่ใช้ app.run () ในการผลิตอยู่ดี app.run () ใช้สำหรับการพัฒนาและทดสอบแอปพลิเคชันของคุณในขณะที่พัฒนาเท่านั้น มีหลายวิธีในการเรียกใช้ Flask ในการผลิตสามารถดูเพิ่มเติมได้ที่นี่เช่นflask.pocoo.org/docs/quickstart/#deploying-to-a-web-serverและถ้าคุณปรับใช้อย่างนั้นแล้ว (ฉันเข้าใจผิด คำถาม) วิธีหยุดให้บริการคำขอที่มาถึง Flask คือหยุดเซิร์ฟเวอร์ http ที่ให้บริการ
Ignas Butėnas

คำตอบ:


135

หากคุณเพิ่งเรียกใช้เซิร์ฟเวอร์บนเดสก์ท็อปของคุณคุณสามารถเปิดเผยจุดสิ้นสุดเพื่อฆ่าเซิร์ฟเวอร์ได้ (อ่านเพิ่มเติมที่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' โดยไม่ต้องใช้บริบทการร้องขอ
akatkinson

5
ฉันต้องเปลี่ยนวิธีเส้นทางเป็น 'GET' เพื่อให้มันใช้งานได้
CS

6
เพื่อความสมบูรณ์คำตอบนี้ไม่มีฟังก์ชันที่คุณจะเรียกนอกบริบทการร้องขอเพื่อทำการปิดระบบซึ่งจะไม่มีอะไรมากไปกว่าคำขอ HTTP ไปยังเซิร์ฟเวอร์ (ซึ่งสามารถมาจาก / ไปยัง localhost)
JamesHutchison

1
ด้วยmethods='POST'ฉันได้รับ405 Method not allowedข้อผิดพลาดในขณะที่วิธีการ = 'รับ' มันทำงานตามที่ @CS แนะนำ
Agile Bean

1
ไม่ได้ผลสำหรับฉันใน AWS Elastic Beanstalk ทำงานได้ดีในท้องถิ่น
Andrey Bulezyuk

36

ฉันทำมันแตกต่างกันเล็กน้อยโดยใช้เธรด

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

ฉันใช้มันเพื่อทำการทดสอบ end to end สำหรับ api ที่สงบซึ่งฉันสามารถส่งคำขอโดยใช้ไลบรารีการร้องขอ python


2
ฉันไม่ได้จัดการเพื่อให้สิ่งอื่นทำงานได้ แต่โซลูชันนี้ใช้งานได้ดี! ขอบคุณมาก! สำหรับคนอื่น ๆ : มันยังใช้งานได้กับกระติกน้ำที่สงบ!
Jorrick Sleijster

ดูเหมือนว่าจะบล็อกบน windows จนกว่าฉันจะกดคำขออื่น ...
Claudiu

ฉันมีปัญหาเดียวกันกับ @Claudiu ยกเว้นบน Linux ที่มี python 3.6.2
micahscopes

ฉันไม่รู้ว่าเหตุใดจึงไม่ยอมรับสิ่งนี้ แต่ดูเหมือนว่าจะสะอาดที่สุดและใช้งานได้ดีโดยไม่ต้องพึ่งพาเพิ่มเติม ขอบคุณมาก.
Eric Reed

22

นี่เป็นเธรดเก่าไปหน่อย แต่ถ้ามีคนทดลองเรียนรู้หรือทดสอบแอพขวดพื้นฐานเริ่มจากสคริปต์ที่ทำงานอยู่เบื้องหลังวิธีที่เร็วที่สุดในการหยุดมันคือการฆ่ากระบวนการที่ทำงานบนพอร์ตที่คุณกำลังเรียกใช้แอพของคุณ บน. หมายเหตุ: ฉันทราบว่าผู้เขียนกำลังมองหาวิธีที่จะไม่ฆ่าหรือหยุดแอป แต่สิ่งนี้อาจช่วยคนที่กำลังเรียนรู้

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ก่อนที่กระบวนการจะถูกฆ่า
Udo E.

17

วิธีการของฉันสามารถดำเนินการผ่าน bash terminal / console

1) เรียกใช้และรับหมายเลขกระบวนการ

$ ps aux | grep yourAppKeywords

2a) ฆ่ากระบวนการ

$ kill processNum

2b) ฆ่ากระบวนการหากด้านบนไม่ทำงาน

$ kill -9 processNum

10
ฉันเกือบจะแน่ใจว่าคำถามไม่ใช่ "วิธีการฆ่ากระบวนการ" และปัญหาคือการทำ ctrl + c ไม่ได้ฆ่ามัน Btw ฉันใช้kill -9 `lsof -i:5000 -t`cuz ไม่เพียงแค่ 1 แอพเท่านั้นที่สามารถใช้พอร์ตได้และเป็นเรื่องง่าย
m3nda

10

ตามที่คนอื่น ๆ ชี้ให้เห็นคุณสามารถใช้ได้werkzeug.server.shutdownจากตัวจัดการคำขอเท่านั้น วิธีเดียวที่ฉันพบในการปิดเซิร์ฟเวอร์ในเวลาอื่นคือการส่งคำขอถึงตัวคุณเอง ตัวอย่างเช่น/killตัวจัดการในข้อมูลโค้ดนี้จะฆ่าเซิร์ฟเวอร์ dev เว้นแต่จะมีคำขออื่นเข้ามาในช่วงวินาทีถัดไป:

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

นี่เป็นคำถามเก่า แต่ googling ไม่ได้ให้ข้อมูลเชิงลึกเกี่ยวกับวิธีการทำสิ่งนี้ให้สำเร็จ

เพราะฉันอ่านโค้ดที่นี่ไม่ถูก! (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
...

ไม่ต้องส่งคำร้องเอง


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 ล้มเหลว: การเชื่อมต่อถูกรีเซ็ต" อาจเห็นเรียกใช้ฟังก์ชันหลังจากที่ Flask ส่งกลับการตอบสนองเพื่อแก้ปัญหา
samm

@samm ข้อสรุปจากคำถามนั้นเป็นไปไม่ได้เว้นแต่คุณจะเริ่มหัวข้ออื่นใช่ไหม? แล้วคุณจะปิดเซิร์ฟเวอร์ขวดจากเธรดอื่นได้อย่างไร?
Jurgy

2

คุณสามารถใช้วิธีการร้อง

app.do_teardown_appcontext()

นี่เป็นการคาดเดาที่ไม่ถูกต้อง ฟังก์ชั่นนี้ไม่ได้หยุดการทำงานของแอป แต่จะเรียกโดยอัตโนมัติเพื่อปล่อยบริบทบางอย่างเมื่อสิ้นสุดการประมวลผลคำขอแต่ละรายการ flask.palletsprojects.com/th/1.1.x/api/…
rgov

2

หากคุณกำลังทำงานบน CLI และมีเพียงหนึ่งแอปขวด / ขั้นตอนการทำงาน (หรือมากกว่าคุณต้องการเพียงแค่ต้องการที่จะฆ่าใด ๆกระบวนการขวดที่ทำงานบนระบบของคุณ) คุณสามารถฆ่ามันด้วย:

kill $(pgrep -f flask)


1

หากคุณอยู่นอกการจัดการคำขอตอบกลับคุณยังสามารถ:

import os
import signal

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

1

หากมีคนอื่นกำลังมองหาวิธีหยุดเซิร์ฟเวอร์ Flask ภายในบริการ win32 - นี่คือ เป็นการผสมผสานที่แปลกประหลาดของหลาย ๆ วิธี แต่ก็ใช้ได้ดี แนวคิดหลัก:

  1. สิ่งเหล่านี้คือshutdownจุดสิ้นสุดที่สามารถใช้สำหรับการปิดเครื่องอย่างสง่างาม หมายเหตุ : ขึ้นอยู่กับrequest.environ.getสิ่งที่ใช้งานได้เฉพาะในบริบทของคำขอเว็บเท่านั้น ( @app.routeฟังก์ชันภายใน)
  2. SvcStopวิธีการของ win32service ใช้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')

0

อินสแตนซ์ Google Cloud VM + แอป Flask

ฉันโฮสต์แอปพลิเคชัน Flask บน Google Cloud Platform Virtual Machine ฉันเริ่มใช้แอพโดยใช้python main.pyแต่ปัญหาคือ ctrl + c ไม่ทำงานเพื่อหยุดเซิร์ฟเวอร์

คำสั่งนี้$ sudo netstat -tulnp | grep :5000ยุติเซิร์ฟเวอร์

แอป My Flask ทำงานบนพอร์ต 5000 ตามค่าเริ่มต้น

หมายเหตุ: อินสแตนซ์ VM ของฉันกำลังทำงานบน 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')

หากคุณประสบปัญหากับไอคอน Fav / การเปลี่ยนแปลงในการโหลด index.html (เช่นเวอร์ชันเก่าจะถูกแคชไว้) ให้ลอง "ล้างข้อมูลการท่องเว็บ> รูปภาพและไฟล์" ใน Chromeด้วย

ทำทุกอย่างข้างต้นและฉันได้รับไอคอน Fav ในที่สุดก็โหลดเมื่อเรียกใช้แอพ Flask


-3

สำหรับ Windows มันค่อนข้างง่ายที่จะหยุด / ฆ่าเซิร์ฟเวอร์ขวด -

  1. ไปที่ Task Manager
  2. ค้นหา flask.exe
  3. เลือกและสิ้นสุดกระบวนการ

14
ปุ่มเปิด / ปิดบนคอมพิวเตอร์ของคุณก็มีประสิทธิภาพเช่นกันฮ่าฮ่า
Michael Green
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.