0

我发现 Miguel Grinberg 的工作非常鼓舞人心,因此我选择在一个大学项目中使用 Flask 和 FlaskSocketIO 库来制作一些 Java 工具(相当复杂的东西,但出于好奇,我正在研究这个项目)。

基本上,python 部分只是创建一个命令,在后台线程中执行它并将每个日志推送到屏幕,这要归功于这个伟大的 Flask SocketIO 库。

所描述的所有内容都很好,但是当我想通过第二次调用相同的 URL 来重新启动任务时遇到问题,我收到了这个错误,我没有找到任何线程,所以我认为它是微不足道的或非常讨厌的(但是中间什么都没有!)。有人有想法吗?

File "/Library/Python/2.7/site-packages/eventlet/wsgi.py", line 485, in handle_one_response
write(b'')
File "/Library/Python/2.7/site-packages/eventlet/wsgi.py", line 380, in write
raise AssertionError("write() before start_response()")
AssertionError: write() before start_response()

而且,当然,这是我的代码(灵感来自 Miguel 的示例,因为我无法自己设置它......)

#!/usr/bin/env python

async_mode = None

if async_mode is None:
    try:
        import eventlet
        async_mode = 'eventlet'
    except ImportError:
        pass

    if async_mode is None:
        try:
            from gevent import monkey
            async_mode = 'gevent'
        except ImportError:
            pass

    if async_mode is None:
        async_mode = 'threading'

    print('async_mode is ' + async_mode)

# monkey patching is necessary because this application uses a background
# thread
if async_mode == 'eventlet':
    import eventlet
    eventlet.monkey_patch()
elif async_mode == 'gevent':
    from gevent import monkey
    monkey.patch_all()

import time
from threading import Thread
import subprocess
from os import chdir

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

import InstrumentationScripts as IS


app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode=async_mode)
thread = None


def background_thread():
    time.sleep(1)
    socketio.emit('my response',
                  {'data': "Thread Started", 'count': 0},
                  namespace='/test')
    cb = IS.CommandBuilder()
    args = cb.createCommand().split()
    chdir(cb.InstrumentationPepDirectory)
    process = subprocess.Popen(args, stdout=subprocess.PIPE)
    for out in iter(process.stdout.readline, b""):
        out = '<pre>' + out + '</pre>'
        socketio.emit('my response', {'data': out, 'count':0}, namespace='/test')
        time.sleep(0.001)
    socketio.emit('my response',
                  {'data': "Thread Finished", 'count': 0},
                  namespace='/test')


@app.route('/')
def index():
    global thread
    if thread is None:
        thread = Thread(target=background_thread)
        thread.daemon = True
        thread.start()
    return render_template('index.html')


@socketio.on('connect', namespace='/test')
def test_connect():
    emit('my response', {'data': 'Connected', 'count': 0})


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

如果你需要完整的代码,这里是repo

4

1 回答 1

1

所以,感谢 Miguel 的评论,我想出了一个解决方案,我认为它可以用于其他类似的问题:

我创建了一个Button来启动该过程。这Button通过 WebSocket 值发送到服务器(a Boolean)。当服务器接收到这个信号时,它启动线程True并向一个全局字典添加一个值,该字典存储所有正在运行的线程(它们的状态:正在运行(True)或未运行( False))。

为了区分所有会话,我使用request.sid示例中提到的 this(请注意,这request.sid不能从 notsocketio方法中检索(至少,我没有成功))。

为了更好地理解,字典看起来像这样:

dict = {"sessionId1":True, "sessionId2": False}

它表明会话 1 的线程正在运行,而会话 2 的线程没有运行(很明显,对吧?)。

然后,我将 传递request.sidThreadas 参数,以便它可以检查 是否Thread已从外部剪切。然后,在显示输入的循环中,我检查是否设置为Boolean,如果它设置为False我切断进程和线程(通过终止进程并结束方法)。

这就是理论,这里是工作代码:

#!/usr/bin/env python

async_mode = None

if async_mode is None:
    try:
        import eventlet
        async_mode = 'eventlet'
    except ImportError:
        pass

    if async_mode is None:
        try:
            from gevent import monkey
            async_mode = 'gevent'
        except ImportError:
            pass

    if async_mode is None:
        async_mode = 'threading'

    print('async_mode is ' + async_mode)

# monkey patching is necessary because this application uses a background
# thread
if async_mode == 'eventlet':
    import eventlet
    eventlet.monkey_patch()
elif async_mode == 'gevent':
    from gevent import monkey
    monkey.patch_all()

import time
from threading import Thread
import subprocess
from os import chdir
import os
import signal

from flask import Flask, render_template, send_file, request, session
from flask_socketio import SocketIO, emit

import InstrumentationScripts as IS


#TODO Emit only to the client who asked for instrumentation

app = Flask(__name__, static_url_path='')
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode=async_mode)
thread = None

thread_map = {}


def background_thread(sid):
    time.sleep(1)
    socketio.emit('my response',
                  {'data': "Thread Started", 'count': 0},
                  namespace='/test')
    cb = IS.CommandBuilder()
    args = cb.createCommand().split()
    chdir(cb.InstrumentationPepDirectory)
    process = subprocess.Popen(args, stdout=subprocess.PIPE)
    for out in iter(process.stdout.readline, b""):
        if (thread_map[sid]):
            out = '<pre>' + out + '</pre>'
            socketio.emit('my response', {'data': out, 'count':0}, namespace='/test')
            time.sleep(0.001)
        else:
            os.kill(process.pid, signal.SIGUSR1)
            print 'Java Process was killed'
            break
    socketio.emit('my response',
                  {'data': "Thread Finished", 'count': 0},
                  namespace='/test')
    thread_map[sid] = False



@app.route('/')
def index():
    return render_template('index.html')


@socketio.on('connect', namespace='/test')
def test_connect():
    emit('my response', {'data': 'Connected\n', 'count': 0})

@socketio.on('disconnect', namespace='/test')
def test_disconnect():
    thread_map[request.sid] = False
    print('Client disconnected')

@socketio.on('startInstrumentation', namespace='/test')
def test_start_stop(message):
    if message['data']:
        thread = Thread(target=background_thread, args=(request.sid,))
        thread.daemon = True
        thread.setName(request.sid)
        thread_map[request.sid] = True
        thread.start()
    else:
        thread_map[request.sid] = False

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

这里是最小的 HTML:

<!DOCTYPE HTML>
<html>
<head>
    <title>Flask-SocketIO Test</title>
    <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script type="text/javascript" src="https://cdn.socket.io/socket.io-1.3.7.js"></script>
    <script type="text/javascript" charset="utf-8">
        $(document).ready(function(){
            namespace = '/test'; // change to an empty string to use the global namespace

            // the socket.io documentation recommends sending an explicit package upon connection
            // this is specially important when using the global namespace
            var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);

            // event handler for server sent data
            // the data is displayed in the "Received" section of the page
            socket.on('my response', function(msg) {
                var log = $('#log');
                log.append(msg.data);
                window.scrollTo(0,document.body.scrollHeight);
            });

            // event handler for new connections
            socket.on('connect', function() {
                socket.emit('my event', {data: 'I\'m connected!'});
            });
            var clicked = false;
           $('form#go').submit(function(event) {
               clicked = !clicked;
                socket.emit('startInstrumentation',  {data: clicked});
               if (clicked){
                   while ($('#log').firstChild) {
                       $('#log').removeChild($('#log').firstChild);
                   }
               }
                return false;
            });
        });
    </script>
</head>
<body>
    <h2>Receive:</h2>
    <form id="go" method="POST" action='#'>
        <input type="submit" value="Go!">
    </form>
    <div id="log"></div>
</body>
</html>

希望能帮助到你。

于 2015-11-26T17:01:46.787 回答