Flask SocketIO Eventlet第二次同时读取错误

时间:2018-05-23 21:09:18

标签: multithreading flask flask-socketio eventlet

我正在尝试使用Flask-Socketio连接到3个独立的RabbitMQ队列: - 1侦听配置更新消息 - 和2在数据库中定义 在服务器启动时,我连接到数据库,获取配置,并启动消费者。然后,在Web前端中,如果更改了其中一个配置设置,则会将这些更改写入数据库,并将配置更新消息添加到第一个RabbitMQ队列。理想情况下,这将触发我当前正在运行的Pika消费者的关闭,该线程的加入,以及使用新配置信息重新启动另一个线程。

我刚刚提出的所有内容都在起作用,但在第一次关闭消费者的尝试中,我总是得到错误:

There was an error stopping the consumer: Second simultaneous read on fileno x detected.  
Unless you really know what you're doing, make sure that only one greenthread can read any particular socket.
Consider using a pools.Pool. If you do know what you're doing and want to disable this error, call eventlet.debug.hub_prevent_multiple_readers(False)...

消费者最终关闭并重新启动,但是,我想了解为什么会发生这种情况,以及如何更改我的代码以阻止它。总是发生错误的地方是这两个函数之间的交接,第一个在我的Consumer类中,第二个在我的Queue类中:

def run(self):        
     while True:
        self.go = True
        self.message_queue = Queue(self.configs, self.go, self.mongo_config)
        self.message_queue.start()
        # here I wait for an event which I set when the config is updated
        self.event.wait() 
        self.go = False
        setattr(self.message_queue, 'go', False)
        new_config = self.refresh_configs()
        setattr(self, 'configs', new_config)
        # when this is called, it should close the existing connection and join the thread
        self.message_queue.refresh_connection()
        self.message_queue.join()

def refresh_connection(self):
    while True:
        if not self.go:
            break
        self.rmq_connection = rabbitmq_consumer(...)
        self.rmq_connection.start_consuming()
    self._lock.acquire()
    try:
        # right here is where the second read error occurs
        self.rmq_connection.stop_consuming()
        self.rmq_connection.close()
    except Exception as e:
        print('There was an error stopping the consumer: {0}'.format(e))
    self._lock.release()

下面是一个更完整的代码示例,以防有助于阐明问题。

thread = None
thread_lock = Lock()
event = Event()

class Queue(Thread):
    def __init__(self, configs, go, outbound):
        Thread.__init__(self)
        self._lock = eventlet.semaphore.Semaphore(1)
        self.go = go
        self.configs = configs
        self.outbound = outbound
        ...
        self.rmq_connection = None

    def on_rmq_message(self, ...):
        ...
        self._lock.acquire()
        socketio.emit('eventEmit', {'data': results}, namespace='/')
        result = rabbitmq_producer(...)
        self._lock.release()

    def refresh_connection(self):
        while True:
            if not self.go:
                break
            self.rmq_connection = rabbitmq_consumer(...)
            self.rmq_connection.start_consuming()
        self._lock.acquire()
        try:
            self.rmq_connection.stop_consuming()
            self.rmq_connection.close()
        except Exception as e:
            print('There was an error stopping the consumer: {0}'.format(e))
        self._lock.release()

    def run(self):
        self.refresh_connection()


class Consumer(Thread):
    def __init__(self, configs, event, channel, mongo_config):
        Thread.__init__(self)
        self.configs = configs
        self.mongo_config = mongo_config
        self.event = event
        self.message_queue = None
        self.go = None
        ...

    def refresh_configs(self):
        r = None
        mconnection = connect_mongodb(...)
        results = retrieve(...)
        for result in results.data:
            if result.get('channel') == self.channel:
                r = result
        return r

    def run(self):
        while True:
            self.go = True
            self.message_queue = Queue(self.configs, self.go, self.mongo_config)
            self.message_queue.start()
            self.event.wait()
            self.go = False
            setattr(self.message_queue, 'go', False)
            new_config = self.refresh_configs()
            setattr(self, 'configs', new_config)
            self.message_queue.refresh_connection()
            self.message_queue.join()


class Producer(Thread):
    def __init__(self, configs, event):
        Thread.__init__(self)
        self._lock = eventlet.semaphore.Semaphore(1)
        self.configs = configs
        self.event = event
        ...
        self.channel = self.configs.get('channel', None)
        self.config_consumer = None

    def on_config_message(self, ...):
        ...
        self._lock.acquire()
        socketio.emit('configEmit', {'data': results}, namespace='/')
        self._lock.release()
        self.event.set()
        self.event.clear()

    def run(self):
        self.config_consumer = rabbitmq_consumer(...)
        self.config_consumer.start_consuming()


def init_connections():
    with app.test_request_context('/'):
        mconnection = connect_mongodb(...)

        results = retrieve(mconnection.engine, mongo_collection, mongo_db, cursor=False)
        ...

        t1 = Producer(configs, event)
        for result in results.data:
            config = {
                ...
            }
            t2 = Consumer(result, event, result['channel'], config)
            t2.start()
        t1.start()
        t1.join()
        t2.join()


@socketio.on('connect')
def handle_connection():
    global thread
    socketio.emit('connectEmit', {'data': 'Connected!'}, namespace='/')
    with thread_lock:
        if thread is None:
            thread = socketio.start_background_task(target=init_connections)

感谢您提供任何帮助。

0 个答案:

没有答案