2016-11-23 5 views
0

Ich möchte z-Prozess beenden, wenn ich www.xxx.xx/stopwork im Browser eingeben.Flask, Assertion Fehler

ich versucht habe:

@app.route('/dowork',methods=['POST']) 

def dowork(): 
............... 
     def workit(): 
     ................... 

     z = Process(target=workit) 


     z.start() 

     @app.route('/stopwork', methods=['POST']) 

     def stopwork(): 

     z.terminate() 

     return redirect('/') 

return redirect('/') 

Es funktioniert wie es sollte - Prozess gestoppt wird. Wenn ich aber nochmal vom Browser aus anrufen/runterladen will. DoWork() startet wieder, aber ich habe einen Fehler im Browser:

Internal Server Error

Der Server hat einen internen Fehler festgestellt und konnte Ihre Anfrage abzuschließen. Entweder ist der Server überlastet oder es liegt ein Fehler in der Anwendung vor.

Und der Fehler aus dem App:

bestehende Endpunkt Funktion:% s '% Endpunkt) AssertionError: View-Funktion Mapping eine vorhandene Endpunkt-Funktion zu überschreiben: Gesperr

Gibt es eine Möglichkeit zu setzen ein Endpunkt als leer nach z.terminate ausgeführt wird? Also gibt es später kein Überschreiben?

+0

was ist das 'def (workit)'? und ist es innerhalb 'def downward()'? – ettanany

+0

Ich habe bearbeitet. Jetzt sollte es klarer sein. Ja, es ist innerhalb von dowork(). Wie Sie sehen können, wird workit() zu einem Prozess. – Lucas

+0

Es ist schwer, Ihrem Code zu folgen, da Ihr Einzug falsch ist, aber irgendwo in einem Funktionsaufruf '@ app.route ('/ stopwork', Methoden = ['POST'])'. Wenn diese Zeile zum ersten Mal ausgeführt wird, registriert sie den Endpunkt. Beim zweiten Mal versucht es den Endpunkt erneut zu registrieren und schlägt fehl. Sie sollten "stopwork" auf derselben Ebene wie doork definieren, so dass Sie den Endpunkt nur einmal registrieren. – dirn

Antwort

0

Es funktioniert nicht wie es sollte. Nicht ein einziges Mal. Es scheint so zu sein, dass es funktioniert, aber die Web-Anwendung garantiert nicht einmal einen einzelnen Thread, nicht einmal einen einzigen Prozess. Daher kann die Stoppanforderung von einem anderen Thread oder sogar einem anderen Prozess als der Startanforderung behandelt werden.

Sie können nicht erwarten, dass ein Wert aus einer Anfrage in einer anderen Anfrage verfügbar ist, es sei denn, Sie haben diese explizit beibehalten. In einem Cookie oder einer Datenbank. Eine Process Instanz kann nicht auf diese Weise beibehalten werden, da ein Teil ihres Werts ein Prozess auf Betriebssystemebene ist. Sie könnten ihre Prozess-ID und möglicherweise ihre Elternprozess-ID speichern, um sicher zu sein, dass dieses Paar bei der Stoppanforderung erkannt wird. Hier ist ein Beispiel dafür, wie ein Session-Cookie für diesen Einsatz:

from multiprocessing import Process 
from time import sleep 

import psutil 
from flask import Flask, redirect, session, url_for 


app = Flask(__name__) 
app.secret_key = 'f\xa9\x85\xf0\xe9\x982\xc4\xf8\xa0k\x91\xa7\xef\x12y' 


@app.route('/') 
def index(): 
    return (
     '<form action="/work/start" method="POST">' 
      '<input type="submit" value="Start">' 
     '</form>' 
     '<form action="/work/stop" method="POST">' 
      '<input type="submit" value="Stop">' 
     '</form>' 
    ) 


def work_it(): 
    while True: 
     print('working...') 
     sleep(1) 


@app.route('/work/start', methods=['POST']) 
def start_work(): 
    if 'work_process' not in session: 
     process = Process(target=work_it) 
     process.start() 
     pid = process.pid 
     parent_pid = psutil.Process(process.pid).parent().pid 
     session['work_process'] = (parent_pid, pid) 
    return redirect(url_for('index')) 


@app.route('/work/stop', methods=['POST']) 
def stop_work(): 
    if 'work_process' in session: 
     parent_pid, pid = session['work_process'] 
     try: 
      process = psutil.Process(pid) 
      if process.parent().pid == parent_pid: 
       process.terminate() 
     except psutil.NoSuchProcess: 
      pass 
     session.pop('work_process') 
    return redirect(url_for('index')) 

Es nutzt psutil die Eltern Prozesse PID zu erhalten und den Prozess zu beenden.

Diese "Lösung" weist einige Mängel auf. Sie können einen Prozess per Browser oder sogar mehrere aus demselben Browser starten, wenn der Browser es erlaubt, die Cookies von verschiedenen Tabs/Fenstern zu trennen. Firefox tut dies mit der privaten Fensterfunktion. Und um einen Prozess zu stoppen, wird der Cookie benötigt. Wenn Sie also Cookies blockieren oder löschen, ist es nicht mehr möglich, den Vorgang mit der Schaltfläche Stop zu stoppen. Wieder Firefox als Beispiel: Wenn Sie ein privates Fenster schließen, sind seine Cookies weg.

Für eine bessere oder sogar echte Lösung müssen Sie die Daten speichern, um einen gestarteten Prozess auf dem Server in einer Datenbank zu identifizieren. Da Flask datenbankunabhängig ist, habe ich kein Beispiel gefunden, da Sie in Ihrer Webanwendung einen völlig anderen Datenbankansatz verwenden können, als ich für das Beispiel gewählt hätte.

+0

Ausgezeichnete Arbeit, BlackJack, du hast meine Augen für dieses Problem geöffnet. Es wäre sehr lehrreich zu sehen, wie Sie eine Lösung mit MySQL-Datenbank anwenden würden. – Lucas