Ответ 1
Мне удалось решить эту проблему путем установки нескольких функций Python, что заставляет Python использовать функции Eventlet вместо собственных. Таким образом, фоновые потоки отлично работают с eventlet.
Я в ситуации, когда получаю сообщение от клиента. Внутри функции, которая обрабатывает этот запрос (@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')
Это заставляет приложение не использовать флажок, хотя он установлен.
Однако это не применимое решение для меня, как использование "threading", поскольку async_mode отказывается принимать двоичные данные. Каждый раз, когда я отправляю некоторые двоичные данные от клиента на сервер, он говорит:
WebSocket transport not available. Install eventlet or gevent and gevent-websocket for improved performance.
Третий вариант: использование gevent async_mode для меня не работает, а gevent еще не поддерживает python 3.
Итак, любые другие предложения?
Мне удалось решить эту проблему путем установки нескольких функций Python, что заставляет Python использовать функции Eventlet вместо собственных. Таким образом, фоновые потоки отлично работают с eventlet.
У меня та же проблема. Но я думаю, что выяснил, в чем дело.
При запуске 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 выберет правильный способ запуска потока.