28

私はクライアントからメッセージを受け取る状況にあります。そのリクエストを処理する関数 (@socketio.on) 内で、重い作業が行われる関数を呼び出したいと思います。これにより、メイン スレッドがブロックされることはありません。クライアントは、作業が完了すると通知されると見なされます。したがって、私は新しいスレッドを開始します。

ここで、非常に奇妙な動作に遭遇しました。メッセージがクライアントに届かないのです。ただし、コードはメッセージが送信される特定の場所に到達します。さらに驚くべきことは、メッセージがクライアントに送信される以外にスレッドで何も起きていない場合、応答が実際にクライアントに到達するという事実です。

要約すると、メッセージが送信される前に計算集約的な何かが発生した場合、メッセージは配信されていません。

ここここで述べられているように、スレッドからクライアントにメッセージを送信することはまったく問題ではありません。

これまでに示したすべての例で、サーバーはクライアントから送信されたイベントに応答します。ただし、一部のアプリケーションでは、サーバーがメッセージの発信元である必要があります。これは、バックグラウンド スレッドなど、サーバーで発生したイベントの通知をクライアントに送信するのに役立ちます。

これがサンプルコードです。コメントのシャープ記号 (#) を削除すると、メッセージ ('foo from thread') はクライアントに到達しません。

from flask import Flask
from flask.ext.socketio import SocketIO, emit
app = Flask(__name__)
socketio = SocketIO(app)

from threading import Thread
import time 

@socketio.on('client command')
def response(data):
    thread = Thread(target = testThreadFunction)
    thread.daemon = True
    thread.start()

    emit('client response', ['foo'])

def testThreadFunction():
#   time.sleep(1)

    socketio.emit('client response', ['foo from thread'])

socketio.run(app)

Python 3.4.3、Flask 0.10.1、flask-socketio1.2、eventlet 0.17.4 を使用しています。

このサンプルをコピーして .py ファイルに貼り付けると、その動作をすぐに再現できます。

誰かがこの奇妙な振る舞いを説明できますか?

アップデート

eventletのバグのようです。私が行った場合:

socketio = SocketIO(app, async_mode='threading')

インストールされているにもかかわらず、アプリケーションがイベントレットを使用しないように強制します。

ただし、これは、async_mode がバイナリ データの受け入れを拒否するため、「スレッド化」を使用するための適切な解決策ではありません。クライアントからサーバーにバイナリデータを送信するたびに、次のように表示されます。

WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.

async_mode として gevent を使用する 3 番目のオプションは機能しません。また、gevent はまだ python 3 をサポートしていません。

他の提案はありますか?

4

3 に答える 3

16

Python がネイティブ関数の代わりにイベントレット関数を使用する原因となるいくつかの Python 関数にモンキーパッチを適用することで、問題を解決することができました。このようにして、バックグラウンド スレッドは eventlet で正常に動作します。

https://github.com/miguelgrinberg/Flask-SocketIO/blob/e024b7ec9db4837196d8a46ad1cb82bc1e15f1f3/example/app.py#L30-L31

于 2016-01-04T19:17:22.950 に答える