【问题标题】:Flask-SocketIO redis subscribeFlask-SocketIO redis 订阅
【发布时间】:2018-10-01 17:32:41
【问题描述】:

我正在使用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)

【问题讨论】:

    标签: python flask redis


    【解决方案1】:

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

    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)
    

    【讨论】:

      【解决方案2】:

      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='/')
      

      【讨论】:

      • 它没有用。在请求上下文之外工作。 “这通常意味着您尝试使用需要活动 HTTP 请求的功能。请参阅有关测试的文档以获取有关如何避免此问题的信息。线程 Thread-1 中的异常:这通常意味着您尝试使用需要的功能以某种方式与当前应用程序对象交互。要解决此问题,请使用 app.app_context() 设置应用程序上下文。有关更多信息,请参阅文档。我会尝试对此进行调查。
      猜你喜欢
      • 1970-01-01
      • 2021-03-26
      • 1970-01-01
      • 2016-05-22
      • 2015-09-04
      • 1970-01-01
      • 2013-10-11
      • 2021-04-12
      • 2016-07-04
      相关资源
      最近更新 更多