具有单个连接和多个通道的多线程Kombu RPC应用程序

时间:2018-11-15 09:38:25

标签: python multithreading rabbitmq rpc kombu

我有一个RPC应用程序,该应用程序具有单个连接,每个线程具有多个通道。当前,如果程序仅在单个线程中使用,它将按预期工作。但是,当添加多个线程时,会发生一系列意外问题,例如:

  • amqp.exceptions.UnexpectedFrame:预期为0xce时收到0x31
  • 无法发送消息
  • 未收到消息
  • ConnectionResetByPeer

存在RPC应用程序的逻辑是因为它可以通过使用一个线程按预期工作,但是使用多个线程才是应用程序开始崩溃的地方。

我的应用的代码为:

from __future__ import absolute_import, unicode_literals

from kombu import Connection, Producer, Consumer, Queue, uuid
import uuid
import threading
import time
from utils import connector


class RpcConnection(object):
    def __init__(self):
        # connector variable is an amqp url to RabbitMq server
        self._connection = Connection(connector, heartbeat=10)
        self._pool = self._connection.ChannelPool(6)

    def connection(self):
        # Returns same connection
        return self._connection

    def new_channel(self):
        # Returns different channel each time from pool of channels
        return self._pool.acquire()


class ExecuteAction(object):
    def __init__(self, connection, channel):
        self._connection = connection
        self._channel = channel

        # Define single response queue on each instance of a thread
        self.callback_queue = Queue(name=str(uuid.uuid4()),
                                    exclusive=True,
                                    auto_delete=True,
                                    durable=False)

        self.correlation_id = None
        self.response = None

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._channel.release()

    def execute_action(self):
        self.correlation_id = str(uuid.uuid4())

        # Send RPC message
        with Producer(self._channel) as producer:
            producer.publish(
                {'message_type': 'test',
                 'speed': int(1)},
                exchange='exchange.rpc',
                routing_key='message.action.rpc',
                reply_to=self.callback_queue.name,
                correlation_id=self.correlation_id,
            )

        # Consume RPC message back from queue
        with Consumer(self._channel,
                      on_message=self.on_response,
                      queues=[self.callback_queue],
                      no_ack=False):
            while self.response is None:
                print('Waiting for response')
                self._connection.drain_events()

        return self.response

    def on_response(self, message):
        if message.properties['correlation_id'] == self.correlation_id:
            print('Message received: {}\n'.format(message.payload))
            self.response = message.payload


class ThreadBase(threading.Thread):
    def __init__(self, connection, channel):
        threading.Thread.__init__(self)

        self._channel = channel
        self._connection = connection

        self.execute_action = None

    def initialise(self):
        # Initialises another class object which sends messages using RPC
        self.execute_action = ExecuteAction(connection=self._connection,
                                            channel=self._channel)

    def run(self):
        while True:
            # Infinite loop that calls execute_action() to send an RPC message every 4 seconds
            self.execute_action.execute_action()
            time.sleep(4)


if __name__ == '__main__':
    rpc_connection = RpcConnection()

    # For thread 1 and thread 2, the same connection is being passed, but each thread has a different channel
    # based on that same connection
    thread1 = ThreadBase(connection=rpc_connection.connection(),
                         channel=rpc_connection.new_channel())

    thread2 = ThreadBase(connection=rpc_connection.connection(),
                         channel=rpc_connection.new_channel())

    thread1.initialise()
    thread2.initialise()

    thread1.setName('Thread 1')
    thread2.setName('Thread 2')

    thread1.start()
    #time.sleep(2)
    thread2.start()

如果添加的time.sleep()为2或更多秒,则该应用程序可以按预期的方式使用多个线程,但这不是理想的结果,因为线程不能彼此并行工作。这里的目标是让多个线程通道使用相同的连接彼此并行工作,以进行和接收RPC调用。

任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:0)

您应该为每个线程使用一个连接,然后在该线程中创建通道。

此外,请注意,您的代码从两个单独的线程调用DateFormatter,但是使用相同的连接。那肯定是有问题的。


注意: RabbitMQ团队监视the rabbitmq-users mailing list,并且有时仅在StackOverflow上回答问题。