Python-Flask-SocketIO envoyer un message à partir du fil: ne fonctionne pas toujours

je suis dans la situation où je reçois un message du client. Dans la fonction qui gère cette requête (@socketio.sur) je veux appeler une fonction où certains gros du travail est fait. Cela ne devrait pas avoir pour effet de bloquer le fil principal et l'on pense que le client sera informé une fois le travail terminé. Ainsi je commence un nouveau fil.

Maintenant je rencontre un étrange comportement: Le message ne parvient jamais au client. Toutefois, le code atteint cet endroit précis où le message est envoyé. Encore plus surprenant est le fait que s'il ne se passe rien dans le thread à l'exception du message envoyé au client alors la réponse trouve son chemin vers le client.

Pour résumer: Si quelque chose d'intensif en informatique se produit avant que le message ne soit envoyé, il n'est pas livré, sinon il est.

Comme il est dit ici et ici, l'envoi de messages à partir d'un thread pour les clients n'est pas un problème tous:

Dans tous les exemples présentés jusqu'à ce point, le serveur répond à un événement envoyé par le client. Mais pour certaines applications, le serveur doit être à l'expéditeur d'un message. Cela peut être utile pour envoyer des notifications aux clients d'événements qui proviennent du serveur, par exemple dans un thread d'arrière-plan.

Voici un exemple de code. Lors de la suppression du commentaire sharps ( # ) le message ('foo from thread') ne trouve pas son chemin vers le client, sinon il n'.

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)

j'utilise Python 3.4.3, fiole 0.10.1, fiole-socketio1.2, eventlet 0.17.4.

Cet exemple peut être copié et collé dans un .py fichier et le comportement peuvent être instantanément reproduits.

quelqu'un Peut-il expliquer cet étrange comportement?

mise à Jour

il semble que ce soit un bug d'evenlet. Si je fais:

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

Il force l'application à ne pas utiliser eventlet bien qu'il est installer.

cependant, ce n'est pas une solution applicable pour moi Car utiliser 'threading' comme async_mode refuse d'accepter des données binaires. Chaque fois que j'envoie des données binaires du client au serveur, il est dit:

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

la troisième option, en utilisant gevent car le mode async_mode ne fonctionne pas pour moi aussi bien que gevent n'a pas encore de support pour python 3.

alors d'autres suggestions?

17
demandé sur Schnodderbalken 2016-01-03 23:29:25

2 réponses

j'ai réussi à résoudre le problème en affichant plusieurs fonctions Python qui font que Python utilise les fonctions eventlet au lieu des fonctions natives. De cette façon, les fils d'arrière-plan fonctionnent très bien avec eventlet.

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

6
répondu Schnodderbalken 2016-01-04 19:17:22

j'ai le même problème. Mais je crois que j'ai trouvé ce sujet.

en démarrant SocketIO avec le code suivant et en créant le threading comme le vôtre, le client ne peut pas recevoir le message émis par le serveur.

socketio = SocketIO(app) socketio.run()

je trouve que le flask_socketio offre une fonction nommée start_background_task de document.

voici sa description.

start_background_task(cible, *args, **kwargs)

Démarrer une tâche de fond en utilisant le modèle asynchrone. Il s'agit d'une fonction utilitaire que les applications peuvent utiliser pour lancer une tâche de fond en utilisant la méthode qui est compatible avec le mode async sélectionné.

Paramètres:

cible-la fonction cible à exécuter. args les arguments à passer à la fonction. kwargs mot – clé arguments à passer à la fonction. Cette fonction retourne un objet compatible avec la classe Thread dans la bibliothèque standard de Python.

La méthode start() sur cet objet est déjà appelé par cette fonction.

alors je remplace mon code thread=threading(target=xxx)socketio.start_background_task(target=xxx)socketio.run() . Le serveur est bloqué dans le thread lorsqu'il est exécuté ,ce qui signifie que la fonction start_background_task n'est retourné qu'après la fin du thread.

puis j'essaie d'utiliser gunicorn pour exécuter mon serveur avec gunicorn --worker-class eventlet -w 1 web:app -b 127.0.0.1:5000

Puis tout fonctionne bien!

Alors laissez start_background_task choisir une bonne façon de commencer une discussion.

0
répondu 殷明军 2018-03-21 16:06:03