Cách dừng ứng dụng bình mà không cần sử dụng ctrl-c


104

Tôi muốn triển khai một lệnh có thể dừng ứng dụng bình bằng cách sử dụng tập lệnh bình. Tôi đã tìm kiếm giải pháp trong một thời gian. Vì khung công tác không cung cấp API "app.stop ()", tôi tò mò về cách viết mã này. Tôi đang làm việc trên Ubuntu 12.10 và Python 2.7.3.


Tại sao bạn cần có thể dừng ứng dụng của mình khỏi một tập lệnh? (Công cụ tốt nhất cho công việc sẽ phụ thuộc vào những gì bạn đang cố gắng làm).
Sean Vieira

Nghiêm túc mà nói, bạn đang cố gắng làm gì ở đây? Nếu bạn đang nói về devserver để phát triển, bạn hoàn toàn ổn nếu dừng nó lại như vậy. Trong sản xuất, bạn không triển khai như vậy và bạn có thể dừng một yêu cầu bất kỳ lúc nào bạn muốn, vì vậy "ứng dụng ngừng chạy".
Ignas Butėnas

@SeanVieira Tôi muốn biết nếu có bất kỳ giải pháp nào để thực hiện việc này.
vic

@IgnasB. Tôi đang phát triển một dịch vụ RESTful trên máy của mình ngay bây giờ. Tôi đang làm việc trong một dự án có lẽ nó sẽ giúp tôi chọn máy nào nên triển khai. Cách duy nhất tôi có thể tìm ra là tắt bằng cách giết quá trình.
vic

3
@vrootic, nhưng bạn sẽ không sử dụng app.run () trong sản xuất. app.run () chỉ được sử dụng để phát triển và để kiểm tra ứng dụng của bạn trong khi phát triển. Có nhiều cách khác nhau để chạy Flask trong sản xuất, bạn có thể tìm thấy thêm ở đây, ví dụ như flask.pocoo.org/docs/quickstart/#deploy-to-a-web-server Và nếu bạn triển khai bằng cách nào đó như vậy rồi (vì vậy tôi đã hiểu nhầm câu hỏi), cách để ngừng cung cấp yêu cầu đến Flask là dừng máy chủ http đang phân phát nó.
Ignas Butėnas

Câu trả lời:


125

Nếu bạn chỉ đang chạy máy chủ trên máy tính để bàn của mình, bạn có thể để lộ một điểm cuối để giết máy chủ (đọc thêm tại 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...'

Đây là một cách tiếp cận khác có nhiều tính năng hơn:

from multiprocessing import Process

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

Hãy cho tôi biết nếu điều này sẽ giúp.


16
Bạn có biết có cách nào để lấy thuộc tính 'werkzeug.server.shutdown' mà không cần bối cảnh yêu cầu không?
akatkinson

5
Tôi đã phải thay đổi phương thức định tuyến thành 'GET' để nó hoạt động.
CS

5
Cho đầy đủ câu trả lời này là mất tích chức năng bạn sẽ gọi bên ngoài của một bối cảnh yêu cầu để thực hiện tắt máy, đó sẽ là gì khác hơn là một yêu cầu HTTP đến máy chủ (có thể có nguồn gốc từ / đến localhost)
JamesHutchison

1
Với methods='POST', tôi gặp 405 Method not allowedlỗi, trong khi với các phương thức = 'GET' ', nó hoạt động như @CS đề xuất.
Agile Bean

1
Không phù hợp với tôi trên AWS Elastic Beanstalk. Làm việc tốt tại địa phương
Andrey Bulezyuk

34

Tôi đã làm nó hơi khác bằng cách sử dụng chủ đề

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

Tôi sử dụng nó để thực hiện các bài kiểm tra end to end cho api còn lại, nơi tôi có thể gửi yêu cầu bằng thư viện yêu cầu python.


2
Tôi đã không quản lý để làm cho những thứ khác hoạt động nhưng giải pháp này hoạt động tuyệt vời! Cảm ơn rất nhiều! Đối với những người khác: nó cũng hoạt động với bình yên tĩnh!
Jorrick Sleijster

Điều này dường như bị chặn trên các cửa sổ cho đến khi tôi nhấn nó với một yêu cầu khác ... bất kỳ cách nào xung quanh điều đó?
Claudiu

Tôi đang gặp vấn đề tương tự như @Claudiu, ngoại trừ trên Linux với python 3.6.2
micahscopes

Tôi không biết tại sao điều này không được chấp nhận, nhưng nó có vẻ là sạch nhất và hoạt động tốt mà không có bất kỳ phụ thuộc nào. Cám ơn rất nhiều.
Eric Reed

17

Đây là một chủ đề hơi cũ, nhưng nếu ai đó đang thử nghiệm, tìm hiểu hoặc thử nghiệm ứng dụng bình cơ bản, bắt đầu từ một tập lệnh chạy trong nền, thì cách nhanh nhất để ngăn chặn nó là giết quá trình đang chạy trên cổng mà bạn đang chạy ứng dụng của mình trên. Lưu ý: Tôi biết rằng tác giả đang tìm cách không giết hoặc dừng ứng dụng. Nhưng điều này có thể giúp ích cho những người đang học.

sudo netstat -tulnp | grep :5001

Bạn sẽ nhận được một cái gì đó như thế này.

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

Để dừng ứng dụng, hãy tắt quá trình

sudo kill 28834

15

Phương pháp của tôi có thể được thực hiện thông qua bash terminal / console

1) chạy và lấy số quá trình

$ ps aux | grep yourAppKeywords

2a) giết quá trình

$ kill processNum

2b) hủy quá trình nếu ở trên không hoạt động

$ kill -9 processNum

9
Tôi gần như chắc chắn rằng câu hỏi không phải là "làm thế nào để giết một quy trình", và vấn đề là thực hiện ctrl + c không giết nó. Btw, tôi sử dụng kill -9 `lsof -i:5000 -t`cuz không ngoài 1 ứng dụng có thể sử dụng cổng và đang được dễ dàng.
m3nda

9

Như những người khác đã chỉ ra, bạn chỉ có thể sử dụng werkzeug.server.shutdowntừ một trình xử lý yêu cầu. Cách duy nhất tôi tìm thấy để tắt máy chủ vào lúc khác là gửi yêu cầu cho chính bạn. Ví dụ: /killtrình xử lý trong đoạn mã này sẽ giết máy chủ nhà phát triển trừ khi một yêu cầu khác đến trong giây tiếp theo:

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..."

2
điều này hoạt động nhưng cảm thấy ... rất khó hiểu . Tôi biết đã được một thời gian, nhưng bạn đã bao giờ tìm thấy một cách dễ dàng để làm điều này mà không cần gửi yêu cầu cho chính mình?
Juicy

7

Đây là một câu hỏi cũ, nhưng googling không cung cấp cho tôi bất kỳ thông tin chi tiết nào về cách thực hiện điều này.

Bởi vì tôi đã không đọc mã ở đây đúng cách! (Doh!) Những gì nó là để nâng cao một RuntimeErrorkhi không có là werkzeug.server.shutdowntrongrequest.environ ...

Vì vậy, những gì chúng ta có thể làm khi không có requestlà tăngRuntimeError

def shutdown():
    raise RuntimeError("Server going down")

và nắm bắt điều đó khi app.run()trả về:

...
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
...

Không cần phải gửi cho mình một yêu cầu.


4

Bạn không phải nhấn "CTRL-C", nhưng bạn có thể cung cấp một điểm cuối thực hiện điều đó cho bạn:

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..." })

Bây giờ bạn chỉ có thể gọi điểm cuối này để tắt máy chủ một cách duyên dáng:

curl localhost:5000/stopServer

Tôi đã kiểm tra mã của bạn, nhưng sau đó os.kill, khách hàng không thể nhận được phản hồi được trả lại. Đối với curl, nó xuất ra "curl: (56) Recv fail: Đã đặt lại kết nối". Cũng có thể xem Thực thi một chức năng sau khi Flask trả về phản hồi để giải quyết nó.
samm

@samm, kết luận từ câu hỏi đó là không thể trừ khi bạn bắt đầu một chủ đề khác, phải không? Sau đó, làm thế nào để bạn tắt máy chủ bình từ chuỗi khác đó?
Phụng vụ

3

Bạn có thể sử dụng phương pháp dưới đây

app.do_teardown_appcontext()

2

Nếu bạn đang làm việc trên CLI và chỉ có một ứng dụng / quy trình bình đang chạy (hoặc đúng hơn, bạn chỉ muốn loại bỏ mọi tiến trình bình đang chạy trên hệ thống của mình), bạn có thể hủy nó bằng:

kill $(pgrep -f flask)


1

Nếu bạn đang ở bên ngoài xử lý yêu cầu-phản hồi, bạn vẫn có thể:

import os
import signal

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

0

Phiên bản Google Cloud VM + Ứng dụng Flask

Tôi đã lưu trữ Ứng dụng Flask của mình trên Máy ảo Google Cloud Platform. Tôi đã khởi động ứng dụng bằng cách sử dụng python main.pyNhưng sự cố là ctrl + c không hoạt động để dừng máy chủ.

Lệnh này $ sudo netstat -tulnp | grep :5000kết thúc máy chủ.

Ứng dụng Flask của tôi chạy trên cổng 5000 theo mặc định.

Lưu ý: Phiên bản VM của tôi đang chạy trên Linux 9.

Nó hoạt động cho điều này. Chưa được thử nghiệm cho các nền tảng khác. Hãy cập nhật hoặc nhận xét nếu nó cũng hoạt động cho các phiên bản khác.


-1

Đối với Windows, khá dễ dàng để dừng / hủy máy chủ bình -

  1. Trình quản lý tác vụ Goto
  2. Tìm flask.exe
  3. Chọn và Kết thúc quá trình

10
Nút nguồn trên máy tính của bạn cũng khá hiệu quả haha
Michael Green
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.