Python-Flask-SocketIO отправить сообщение из потока: не всегда работает

Я в ситуации, когда я получаю сообщение от клиента. В функции, которая обрабатывает этот запрос (@socketio.on) я хочу вызвать функцию, где выполняется тяжелая работа. Это не должно приводить к блокировке основного потока, и клиент считается информированным после выполнения работы. Так я начинаю новую нить.

Теперь я сталкиваюсь с действительно странным поведением: Сообщение никогда не доходит до клиента. Тем не менее, код достигает того конкретного места, где сообщение отправлено. Еще более удивительным является тот факт, что если в потоке ничего не происходит, кроме сообщения, отправляемого клиенту, то ответ фактически находит свой путь к клиенту.

подводя итог: Если что-то вычислительно интенсивное происходит до отправки сообщения, оно не доставляется, иначе это так.

как сказал здесь и здесь отправка сообщений из потока к клиенту-это не проблема на всех:

во всех примерах, показанных до этого момента, сервер отвечает на событие, отправленное клиентом. Но для некоторых приложений сервер должен быть инициатором сообщения. Это может быть полезно для отправки уведомлений клиентам о событиях, которые произошли на сервере, например в фоновом потоке.

вот пример кода. При удалении комментирования sharps ( # ) сообщение ('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, колбу 0.10.1, колбу-socketio1.2, eventlet 0.17.4.

этот образец можно скопировать и вставить в a .файл py и поведение могут быть мгновенно воспроизведены.

может кто-нибудь объяснить это странное поведение?

обновление

похоже, это ошибка eventlet. Если я это сделаю:

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

Это заставляет приложение не использовать eventlet, хотя это установленный.

однако это не применимое решение для меня, поскольку использование "threading", поскольку async_mode отказывается принимать двоичные данные. Каждый раз, когда я отправляю некоторые двоичные данные от клиента на сервер, он говорит:

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

третий вариант, используя gevent в качестве async_mode не работает для меня, а также gevent еще не имеет поддержки python 3.

Итак, любые другие предложения?

2 ответов


мне удалось решить проблему, monkeypatching несколько функций Python, что заставляет Python использовать функции eventlet вместо собственных. Таким образом, фоновые потоки отлично работают с eventlet.

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


у меня такая же проблема. Но, кажется, я понял, в чем дело.

при запуске SocketIO со следующим кодом и создании потока, подобного вашему, клиент не может получить сообщение, которое излучается сервером.

socketio = SocketIO(app) socketio.run()

я узнаю, что flask_socketio предлагает функцию с именем start_background_task из документ.

вот его описание.

start_background_task (target, *args, **kwargs)

запустите фоновую задачу, используя соответствующую асинхронную модель. Это служебная функция, которую приложения могут использовать для запуска фоновой задачи с помощью метода, совместимого с выбранным асинхронным режимом.

параметры:

target-целевая функция для выполнения. args-аргументы для передачи функции. kwargs-аргументы ключевого слова для передачи функции. Эта функция возвращает объект, совместимый с классом Thread в стандартной библиотеке Python.

метод start () для этого объекта уже вызывается этой функцией.

поэтому я заменяю свой код thread=threading(target=xxx) С socketio.start_background_task(target=xxx) затем socketio.run() . Сервер застревает в потоке при запуске в него, что означает, что функция start_background_task возвращается только после завершения потока.

затем я пытаюсь использовать gunicorn для запуска моего сервера с gunicorn --worker-class eventlet -w 1 web:app -b 127.0.0.1:5000

потом все хорошо работает!

поэтому пусть start_background_task выберет правильный способ запуска потока.