使用Python,Pika和RabbitMQ创建延迟(或停放)队列的最简单方法是什么?我见过类似的questions,但Python没有。
我在设计应用程序时发现这是一个有用的想法,因为它允许我们限制需要重新排队的消息。
总有可能会收到超出您可以处理的消息,可能是HTTP服务器速度慢,或者数据库承受的压力太大。
我还发现,在丢失消息零容忍的情况下出现问题时非常有用,而重新排队无法处理的消息可能会解决这个问题。它也可能导致消息一次又一次排队的问题。可能导致性能问题,并记录垃圾邮件。
答案 0 :(得分:85)
我发现在开发我的应用程序时非常有用。因为它为您提供了简单地重新排队邮件的替代方案。这可以轻松降低代码的复杂性,是RabbitMQ中众多强大的隐藏功能之一。
<强>步骤强>
首先,我们需要设置两个基本通道,一个用于主队列,另一个用于延迟队列。在我最后的例子中,我包含了一些不需要的额外标志,但使代码更可靠;例如confirm delivery
,delivery_mode
和durable
。您可以在RabbitMQ manual中找到有关这些的更多信息。
在我们设置了频道之后,我们添加了一个绑定到主频道,我们可以用它来将消息从延迟频道发送到我们的主队列。
channel.queue_bind(exchange='amq.direct',
queue='hello')
接下来,我们需要配置延迟通道,以便在消息到期后将消息转发到主队列。
delay_channel.queue_declare(queue='hello_delay', durable=True, arguments={
'x-message-ttl' : 5000,
'x-dead-letter-exchange' : 'amq.direct',
'x-dead-letter-routing-key' : 'hello'
})
x-message-ttl (消息 - 生存时间)
这通常用于自动删除中的旧邮件 在特定持续时间后排队,但通过添加两个可选参数我们 可以更改此行为,而是确定此参数 以毫秒为单位的消息将在延迟队列中保留多长时间。
此变量允许我们将消息传输到不同的队列 一旦它们过期,而不是删除的默认行为 完全。
此变量确定用于将消息从hello_delay传输到hello队列的Exchange。
发布到延迟队列
当我们完成设置所有基本Pika参数后,您只需使用基本发布将消息发送到延迟队列。
delay_channel.basic_publish(exchange='',
routing_key='hello_delay',
body="test",
properties=pika.BasicProperties(delivery_mode=2))
执行完脚本后,您应该会在RabbitMQ管理模块中看到以下队列。
示例强>
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters(
'localhost'))
# Create normal 'Hello World' type channel.
channel = connection.channel()
channel.confirm_delivery()
channel.queue_declare(queue='hello', durable=True)
# We need to bind this channel to an exchange, that will be used to transfer
# messages from our delay queue.
channel.queue_bind(exchange='amq.direct',
queue='hello')
# Create our delay channel.
delay_channel = connection.channel()
delay_channel.confirm_delivery()
# This is where we declare the delay, and routing for our delay channel.
delay_channel.queue_declare(queue='hello_delay', durable=True, arguments={
'x-message-ttl' : 5000, # Delay until the message is transferred in milliseconds.
'x-dead-letter-exchange' : 'amq.direct', # Exchange used to transfer the message from A to B.
'x-dead-letter-routing-key' : 'hello' # Name of the queue we want the message transferred to.
})
delay_channel.basic_publish(exchange='',
routing_key='hello_delay',
body="test",
properties=pika.BasicProperties(delivery_mode=2))
print " [x] Sent"
答案 1 :(得分:15)
您可以使用RabbitMQ官方插件: x-delayed-message 。
首先,将ez file下载并复制到 Your_rabbitmq_root_path / plugins
其次,启用插件(不需要重启服务器):
headers.put("x-delay", 5000);
最后,使用&#34; x-delay&#34;发布您的消息标题如:
{{1}}
<强>注意:强>
它不能确保您的邮件安全,因为如果您的邮件在您的rabbitmq服务器停机期间到期,很遗憾邮件会丢失。所以当你使用这个方案时,要小心。
中享受它和更多信息答案 2 :(得分:8)
仅供参考,如何在Spring 3.2.x中执行此操作。
<rabbit:queue name="delayQueue" durable="true" queue-arguments="delayQueueArguments"/>
<rabbit:queue-arguments id="delayQueueArguments">
<entry key="x-message-ttl">
<value type="java.lang.Long">10000</value>
</entry>
<entry key="x-dead-letter-exchange" value="finalDestinationTopic"/>
<entry key="x-dead-letter-routing-key" value="finalDestinationQueue"/>
</rabbit:queue-arguments>
<rabbit:fanout-exchange name="finalDestinationTopic">
<rabbit:bindings>
<rabbit:binding queue="finalDestinationQueue"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
答案 3 :(得分:2)
NodeJS实施。
代码中的一切都很清楚。 希望它能节省一些人的时间。
var ch = channel;
ch.assertExchange("my_intermediate_exchange", 'fanout', {durable: false});
ch.assertExchange("my_final_delayed_exchange", 'fanout', {durable: false});
// setup intermediate queue which will never be listened.
// all messages are TTLed so when they are "dead", they come to another exchange
ch.assertQueue("my_intermediate_queue", {
deadLetterExchange: "my_final_delayed_exchange",
messageTtl: 5000, // 5sec
}, function (err, q) {
ch.bindQueue(q.queue, "my_intermediate_exchange", '');
});
ch.assertQueue("my_final_delayed_queue", {}, function (err, q) {
ch.bindQueue(q.queue, "my_final_delayed_exchange", '');
ch.consume(q.queue, function (msg) {
console.log("delayed - [x] %s", msg.content.toString());
}, {noAck: true});
});
答案 4 :(得分:0)
Rabbit队列中的消息可以以两种方式延迟 - 使用QUEUE TTL - 使用Message TTL 如果队列中的所有消息都要延迟一段时间,请使用队列TTL。 如果每条消息都必须按不同的时间延迟,请使用Message TTL。 我用python3和pika模块解释了它。 pika BasicProperties参数'expiration'(以毫秒为单位)必须设置为延迟队列中的延迟消息。 设置到期时间后,将消息发布到delayed_queue(“非消费者等待消费的实际队列”),一旦delayed_queue中的消息到期,消息将使用exchange'amq.direct'路由到实际队列
def delay_publish(self, messages, queue, headers=None, expiration=0):
"""
Connect to RabbitMQ and publish messages to the queue
Args:
queue (string): queue name
messages (list or single item): messages to publish to rabbit queue
expiration(int): TTL in milliseconds for message
"""
delay_queue = "".join([queue, "_delay"])
logging.info('Publishing To Queue: {queue}'.format(queue=delay_queue))
logging.info('Connecting to RabbitMQ: {host}'.format(
host=self.rabbit_host))
credentials = pika.PlainCredentials(
RABBIT_MQ_USER, RABBIT_MQ_PASS)
parameters = pika.ConnectionParameters(
rabbit_host, RABBIT_MQ_PORT,
RABBIT_MQ_VHOST, credentials, heartbeat_interval=0)
connection = pika.BlockingConnection(parameters)
channel = connection.channel()
channel.queue_declare(queue=queue, durable=True)
channel.queue_bind(exchange='amq.direct',
queue=queue)
delay_channel = connection.channel()
delay_channel.queue_declare(queue=delay_queue, durable=True,
arguments={
'x-dead-letter-exchange': 'amq.direct',
'x-dead-letter-routing-key': queue
})
properties = pika.BasicProperties(
delivery_mode=2, headers=headers, expiration=str(expiration))
if type(messages) not in (list, tuple):
messages = [messages]
try:
for message in messages:
try:
json_data = json.dumps(message)
except Exception as err:
logging.error(
'Error Jsonify Payload: {err}, {payload}'.format(
err=err, payload=repr(message)), exc_info=True
)
if (type(message) is dict) and ('data' in message):
message['data'] = {}
message['error'] = 'Payload Invalid For JSON'
json_data = json.dumps(message)
else:
raise
try:
delay_channel.basic_publish(
exchange='', routing_key=delay_queue,
body=json_data, properties=properties)
except Exception as err:
logging.error(
'Error Publishing Data: {err}, {payload}'.format(
err=err, payload=json_data), exc_info=True
)
raise
except Exception:
raise
finally:
logging.info(
'Done Publishing. Closing Connection to {queue}'.format(
queue=delay_queue
)
)
connection.close()
答案 5 :(得分:0)
根据您的情况和需求,我建议采用以下方法,