Flask-SocketIO Redis订阅

时间:2018-10-01 17:32:42

标签: python flask redis

我正在使用https://github.com/miguelgrinberg/Flask-SocketIO来实现WebSocket服务器。

我需要接收来自其他进程的消息(仅订阅),并向特定房间中的客户端发送消息。

但是,当我尝试发送邮件时,出现此错误:

  

无法将消息发送到家庭会议室:在请求上下文之外工作。

这是我的代码:

from flask import Flask, request
from flask_socketio import SocketIO, join_room, leave_room, send, rooms
import json
import eventlet
import logging
import redis
import threading

FORMAT = '%(asctime)-15s - %(message)s'
logging.basicConfig(format=FORMAT)
log = logging.getLogger(__name__)

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode='eventlet')

.
.
.

def _send_task_message():
    try:
        send(json.dumps({"type":"UPDATE_TASK"}), room='home')
    except Exception as e:
        log.error('Could not send message to home room: %s' % str(e)) 

class Listener(threading.Thread):
    def __init__(self, r, channels):
        threading.Thread.__init__(self)
        self.daemon = True
        self.redis = r
        self.pubsub = self.redis.pubsub()
        self.pubsub.psubscribe(channels)

    def work(self, item):
        if isinstance(item['data'], bytes):
            try:
                msg = item['data'].decode('utf-8')
                decode_msg = json.loads(msg)                
                if decode_msg['type'] == 'UPDATE_TASK':
                    _send_task_message()
            except ValueError as e:
                log.error("Error decoding msg to microservice: %s", str(e))

    def run(self):
        for item in self.pubsub.listen():
            self.work(item)


if __name__ == '__main__':

    r = redis.Redis()
    client = Listener(r, ['/bobguarana/socketio'])
    client.start()

    socketio.run(debug=True, app=app, port=8080)

2 个答案:

答案 0 :(得分:0)

If you look at the source code of the flask_socketio.send method,您会看到,如果省略了命名空间,flask_socketio将尝试从当前请求中获取它。但是,当您收到来自Redis的消息时,将不会有任何当前请求来获取名称空间。因此,为了在没有当前请求的情况下发送socketio消息(在Flask中称为request context),您应指定一个名称空间(默认情况下,“ /”为IIRC):

send(json.dumps({"type":"UPDATE_TASK"}), room='home', namespace='/')

答案 1 :(得分:0)

我解决了将应用程序作为参数传递给类并按照错误描述的建议使用它的上下文,但是名称空间也是必需的:

class Listener(threading.Thread):
    def __init__(self, r, channels, app):
    threading.Thread.__init__(self)
    self.daemon = True
    self.redis = r
    self.pubsub = self.redis.pubsub()
    self.pubsub.psubscribe(channels)
    self.app = app

    def work(self, item):
        with app.app_context():
            if isinstance(item['data'], bytes):
                try:
                    msg = item['data'].decode('utf-8')
                    decode_msg = json.loads(msg)                
                    if decode_msg['type'] == 'UPDATE_TASK':
                        send(json.dumps({"type":"UPDATE_TASK"}), room='home', namespace='/')
                    #_send_task_message()
                except ValueError as e:
                    log.error("Error decoding msg to microservice: %s", str(e))

    def run(self):
        for item in self.pubsub.listen():
            self.work(item)

if __name__ == '__main__':

    r = redis.Redis()
    client = Listener(r, ['/bobguarana/socketio'], app)
    client.start()

    socketio.run(debug=True, app=app, port=8080)