Flask stateful backend: переход из состояния в состояние по завершении задачи - PullRequest
0 голосов
/ 13 февраля 2019

Я пытаюсь закодировать бэкэнд в колбе, которая должна выполнить ряд проверок на внешних машинах (check1, check2 и т. Д.).Это последовательный процесс (после проверки n, мы проверяем n + 1), но он требует сохранения «текущего состояния».Например, check1 состоит в опросе ресурса (скручивание каждую секунду) из внешнего API, который возвращает некоторые журналы машины.

Всякий раз, когда бэкэнд находит «некоторую строку» в извлеченных журналах, бэкэнд должен обновитьглобальное состояние бэкэнда, сообщите об этом во внешний интерфейс (чтобы обновить компоненты макета) и перейдите к следующему состоянию.

Опять же, в следующем состоянии бэкэнд проверяет что-то еще, и когда «другая строка»найдено, обновите глобальное состояние, уведомите его, чтобы перейти к новому состоянию и перейти к следующему состоянию.

И так, это основная идея.

У него есть единственный маршрут (/).

Веб-приложение не для производства, оно просто должно работать, и его цель - просто показать, как развивается система (пример мониторинга).Глобальное состояние должно передаваться таким образом, чтобы любой, кто идет по маршруту / видел текущее состояние.

Я новичок в веб-разработке и считаю, что это странное приложение, поэтому оно не так тривиально, как я думал.

Ниже я покажу некоторый код, который представляет:

Состояние 0: сообщает новое состояние (данные) интерфейсу через socketio.Ничего не делает, просто ждет события onClick в кнопке запуска.Когда это происходит, происходит переход в состояние 1.

Состояние 1: сообщает новое состояние (данные) интерфейсу через socketio.Затем выполняет какую-то задачу.Когда задача «успешно» перейдет в следующее состояние 2.

Независимо от состояния, пользователь может изменить параметр (параметр) в веб-интерфейсе пользователя.Я справляюсь с этим через запись socketio внизу.

main.py

from flask import Flask, render_template
from flask_socketio import SocketIO, emit
import time, json

global data
data = {'state': '0', 'param': '50', 'disableStart': 'false', 'disableReset': 'true', 'info': 'Waiting to start'}

def state_1():
    print("[Server.state_1]: In state 1...checking service")
    data['state'] = '1'
    data['disableStart'] = 'true'
    data['disableReset'] = 'true'
    data['info'] = 'Waiting for service is OK'
    socketio.emit('refresh', json.dumps(data), broadcast=True)
    # Checking if service is OK...
    # Below is just a dummy sustitute
    counter = 0
    while counter < 100:
        print("Checking..." + str(counter) + "%")  
        time.sleep(1) 
        counter += 1
    # When service is OK jump to state_2()  


def state_0():
    print("[Server.state_0]: In state 0...waiting to start...")
    data['state'] = '0'
    data['info'] = 'Waiting to start'
    socketio.emit('refresh', json.dumps(data), broadcast=True)

# initialize Flask
app = Flask(__name__)
socketio = SocketIO(app)

@app.route('/')
def index():   
    print("[Server.index]: "  + json.dumps(data))   
    return render_template('index.html') 

@socketio.on('connect')
def on_connect():
    print("[Server.on_connect]: A new connection!")
    print("[Server.on_connect]: " + json.dumps(data))
    # In every new refresh or new tab, we need to display current state
    emit('refresh', json.dumps(data), broadcast=True)
    # Jump to current state
    print("[Server.on_connect]: Jump to state " + data['state'])
    f_name = "state_" + data['state']
    globals()[f_name]()

@socketio.on('start')
def on_start():
    print("[Server.on_start]: Clicked on START button! Jump to state 1")
    state_1()

@socketio.on('update_slide_param')
def on_update_slide_param(paramVal):
    data['param'] = paramVal
    # Setting this param to an external machine...
    print("[Server.on_update_slide_param]: A new value has been set! Jump to state " + data['state'])
    f_name = "state_" + data['state']
    globals()[f_name]()

if __name__ == '__main__':
    socketio.run(app, debug=True)

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Flask SocketIO Test</title>
</head>

<body>
    <p>Some sample code to make sure Flask-SocketIO works...</p>
    <button onclick="onClickStart()" id="startButton">START</button>
    <button onclick="onClickReset()" id="resetButton">RESET</button>
    <input onchange="onSetSliderVal()" class="slider" type="range" id="idSlider" min="50" max="300" value="50"/>
    <p>Value: <span id="idSliderVal"></span></p>


    <script type="text/javascript" charset="utf-8">
        var slider = document.getElementById("idSlider");
        var output = document.getElementById("idSliderVal");
        output.innerHTML = slider.value; // Display the default slider value

        // Update the current slider value (each time you drag the slider handle)
        slider.oninput = function () {
            output.innerHTML = this.value;
        }
    </script>


    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>

    <script type="text/javascript" charset="utf-8">

        var socket = io.connect('http://' + document.domain + ':' + location.port);
        // verify our websocket connection is established
        socket.on('connect', function () {
            console.log('Websocket connected!');
        });

        // onClickStart - emit a message on the 'start' channel to 
        // launch a new demo with default parameters
        function onClickStart() {
            console.log('Starting demo...');
            socket.emit('start');
        };

        // onSetSliderVal - emit a message on the 'update_slide_param' channel to 
        // perform the action and update global 'data'
        function onSetSliderVal() {
            var paramVal = document.getElementById('idSlider').value;
            console.log('Setting a value of ' + paramVal + '...');
            socket.emit('update_slide_param', paramVal);
        };

        socket.on('refresh', function (data) {
            var x = JSON.parse(data); // This is the way we can use our dictionary 'data' as a JSON in JS
            console.log('[refresh] Frontend has updated info! Now we are in state ' + x.state);
            console.log('[refresh] All variables are:'); console.log(x);

            document.getElementById('idSlider').value = x.param;
            document.getElementById("idSliderVal").innerHTML = x.param;
            document.getElementById('startButton').disabled = JSON.parse(x.disableStart); // This is the cleanest way JS converts a "true"/"false" (string) into true/false (boolean)
            document.getElementById('resetButton').disabled = JSON.parse(x.disableReset);

        });

    </script>

</body>

</html>

Проблема:

Если я обновляюсь в состоянии 0, он работает нормально.Во время этого состояния я могу изменить «параметр», и информация обновляется в каждой открытой вкладке браузера.Если я обновлюсь, обновленный параметр будет сохранен.

Затем я нажимаю кнопку запуска, и все в порядке (состояние внешнего интерфейса обновляется, как я хочу).Однако, если я обновлю вкладку, произойдут две вещи:

a.Из средств разработки браузера информация консоли из сокетов исчезает.

b.Функция state_1 () на сервере выполняется снова, и в журналах сервера вы увидите что-то вроде этого:

Проверка ... 2% Проверка ... 16% Проверка ... 3% Проверка ...17% Проверка ... 4%

...

Конечно, кажется, что новый "экземпляр" функции выполняется.

В целом, это будетбыло бы приятно иметь обратную связь.Вы можете быстро протестировать проблему с помощью предоставленного кода (создать venv, установить колбу pip3 flask-socketio, создать папку шаблонов для index.html, а затем просто «python3 main.py»)

...