python 多线程消费 rabbitmq_Python和RabbitMQ-从多个通道监听消费事件的最佳方式?...

“什么是最佳方法”的答案在很大程度上取决于队列的使用模式和您所说的“最佳”是什么意思。既然我还不能对问题发表评论,我就试着提出一些可能的解决办法。

在每个例子中,我假设exchange已经声明了。

螺纹

您可以使用^{}在单个进程中使用来自独立主机上两个队列的消息。

您是对的,因为its own FAQ states,pika不是线程安全的,但是可以通过为每个线程创建到RabbitMQ主机的连接以多线程方式使用它。使用^{}模块在线程中运行此示例,如下所示:import pika

import threading

class ConsumerThread(threading.Thread):

def __init__(self, host, *args, **kwargs):

super(ConsumerThread, self).__init__(*args, **kwargs)

self._host = host

# Not necessarily a method.

def callback_func(self, channel, method, properties, body):

print("{} received '{}'".format(self.name, body))

def run(self):

credentials = pika.PlainCredentials("guest", "guest")

connection = pika.BlockingConnection(

pika.ConnectionParameters(host=self._host,

credentials=credentials))

channel = connection.channel()

result = channel.queue_declare(exclusive=True)

channel.queue_bind(result.method.queue,

exchange="my-exchange",

routing_key="*.*.*.*.*")

channel.basic_consume(self.callback_func,

result.method.queue,

no_ack=True)

channel.start_consuming()

if __name__ == "__main__":

threads = [ConsumerThread("host1"), ConsumerThread("host2")]

for thread in threads:

thread.start()

我已经声明了callback_func作为打印消息体时纯粹使用ConsumerThread.name的方法。它也可能是ConsumerThread类之外的函数。

过程

或者,您可以始终只运行一个进程,每个要使用事件的队列使用使用者代码。import pika

import sys

def callback_func(channel, method, properties, body):

print(body)

if __name__ == "__main__":

credentials = pika.PlainCredentials("guest", "guest")

connection = pika.BlockingConnection(

pika.ConnectionParameters(host=sys.argv[1],

credentials=credentials))

channel = connection.channel()

result = channel.queue_declare(exclusive=True)

channel.queue_bind(result.method.queue,

exchange="my-exchange",

routing_key="*.*.*.*.*")

channel.basic_consume(callback_func, result.method.queue, no_ack=True)

channel.start_consuming()

然后运行:$ python single_consume.py host1

$ python single_consume.py host2 # e.g. on another console

如果您对来自队列的消息所做的工作是CPU-heavy,并且只要您的CPU中的内核数=使用者的数量,那么通常最好使用这种方法-除非您的队列大部分时间是空的,并且使用者不会使用此CPU时间*。

异步

另一种选择是使用一些异步框架(例如^{})并在单个线程中运行整个过程。

您不能再在异步代码中使用BlockingConnection;幸运的是,pika有用于Twisted的适配器:from pika.adapters.twisted_connection import TwistedProtocolConnection

from pika.connection import ConnectionParameters

from twisted.internet import protocol, reactor, task

from twisted.python import log

class Consumer(object):

def on_connected(self, connection):

d = connection.channel()

d.addCallback(self.got_channel)

d.addCallback(self.queue_declared)

d.addCallback(self.queue_bound)

d.addCallback(self.handle_deliveries)

d.addErrback(log.err)

def got_channel(self, channel):

self.channel = channel

return self.channel.queue_declare(exclusive=True)

def queue_declared(self, queue):

self._queue_name = queue.method.queue

self.channel.queue_bind(queue=self._queue_name,

exchange="my-exchange",

routing_key="*.*.*.*.*")

def queue_bound(self, ignored):

return self.channel.basic_consume(queue=self._queue_name)

def handle_deliveries(self, queue_and_consumer_tag):

queue, consumer_tag = queue_and_consumer_tag

self.looping_call = task.LoopingCall(self.consume_from_queue, queue)

return self.looping_call.start(0)

def consume_from_queue(self, queue):

d = queue.get()

return d.addCallback(lambda result: self.handle_payload(*result))

def handle_payload(self, channel, method, properties, body):

print(body)

if __name__ == "__main__":

consumer1 = Consumer()

consumer2 = Consumer()

parameters = ConnectionParameters()

cc = protocol.ClientCreator(reactor,

TwistedProtocolConnection,

parameters)

d1 = cc.connectTCP("host1", 5672)

d1.addCallback(lambda protocol: protocol.ready)

d1.addCallback(consumer1.on_connected)

d1.addErrback(log.err)

d2 = cc.connectTCP("host2", 5672)

d2.addCallback(lambda protocol: protocol.ready)

d2.addCallback(consumer2.on_connected)

d2.addErrback(log.err)

reactor.run()

这种方法会更好,从中消耗的队列越多,用户执行的工作受CPU限制越小*。

Python3

既然您提到了pika,我就把自己局限于基于Python 2.x的解决方案,因为pika还没有移植。

但是,如果您希望移到>=3.3,一个可能的选择是将^{}与AMQP协议(您与RabbitMQ交谈的协议)之一一起使用,例如^{}或^{}。

*-请注意,这些都是非常浅显的提示-在大多数情况下,选择并不是那么明显;什么对您最好取决于队列“饱和”(消息/时间)、接收这些消息后您做了什么工作、在什么环境中运行您的消费者等;除了对所有实现进行基准测试之外,没有其他方法可以确定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值