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 выберет правильный способ запуска потока.