Как создать задержанную очередь в RabbitMQ?
каков самый простой способ создать очередь задержки (или парковки) с Python, Pika и RabbitMQ? Я видел подобное вопросы, но не для Python.
Я считаю это полезной идеей при разработке приложений, так как это позволяет нам дросселировать сообщения, которые нужно снова поставить в очередь.
всегда есть вероятность того, что вы получите больше сообщений, чем сможете обработать, возможно, HTTP-сервер работает медленно, или база данных находится под слишком большим стресс.
Я также нашел его очень полезным, когда что-то пошло не так в сценариях, где есть нулевая толерантность к потере сообщений, и при повторной очереди сообщений, которые не могут быть обработаны, может решить эту проблему. Это также может вызвать проблемы, когда сообщение будет стоять в очереди снова и снова. Потенциально вызывает проблемы с производительностью и спам журнала.
5 ответов
Я нашел это чрезвычайно полезным при разработке моих приложений. Как это дает вам альтернативу для очереди сообщений. Это может легко уменьшить сложность вашего кода и является одной из многих мощных скрытых функций в RabbitMQ.
шаги
Сначала нам нужно настроить два основных канала, один для основной очереди и один для очереди задержки. В моем примере в конце я включаю пару дополнительных флагов, которые не требуются, но делает код более надежным; например,confirm delivery
, delivery_mode
и durable
. Вы можете найти более подробную информацию об этом в RabbitMQ руководство.
после того, как мы настроили каналы, мы добавляем привязку к основному каналу, который мы можем использовать для отправки сообщений из канала задержки в нашу основную очередь.
channel.queue_bind(exchange='amq.direct',
queue='hello')
Далее нам нужно настроить наш канал задержки для пересылки сообщений в основную очередь, как только они будут истeкший.
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-сообщение-ttl (Сообщение - "Время Жить")
это обычно используется для автоматического удаления старых сообщений в очередь после определенной продолжительности, но путем добавления двух необязательных аргументов мы может изменить это поведение и вместо этого определить этот параметр в миллисекундах, как долго сообщения будут оставаться в задержке очередь.
-
эта переменная позволяет переносить сообщение в другую очередь как только они истекли, вместо поведения по умолчанию удаления полностью.
-
эта переменная определяет, какой обмен используется для передачи сообщения из hello_delay в hello очередь.
публикация в очередь задержки
когда мы закончим настройку всех основных параметров Pika, вы просто отправите сообщение в очередь задержки с помощью basic publish.
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"
вы можете использовать официальный плагин RabbitMQ:X-delayed-message .
во-первых, загрузите и скопируйте эз файл на Your_rabbitmq_root_path/Плагины
во-вторых, включите плагин (не нужно перезапускать сервер):
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
наконец, опубликуйте свое сообщение с заголовками" X-delay", такими как:
headers.put("x-delay", 5000);
обратите внимание:
Он не обеспечивает безопасность Вашего сообщения, потому что если ваше сообщение истекает только во время простоя вашего rabbitmq-сервера, к сожалению, сообщение потеряно. Так что будьте осторожны при использовании этой схемы.
наслаждайтесь и больше информации в rabbitmq-задержка-обмен сообщениями
FYI, как это сделать весной 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>
реализация 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});
});
сообщение в очереди кролика может быть задержано двумя способами - использование очереди TTL - использование сообщения TTL Если все сообщения в очереди должны быть задержаны на фиксированное время, используйте очередь TTL. Если каждое сообщение должно быть отложено на различное время, используйте сообщение TTL. Я объяснил это с помощью python3 и модуля pika. аргумент Pika BasicProperties "expiration" в миллисекундах должен быть установлен для задержки сообщения в очереди задержки. После установки срока действия опубликуйте сообщение в delayed_queue ("не фактическая очередь, где потребители ждут, чтобы потреблять") , как только сообщение в delayed_queue истекает, сообщение будет перенаправлено в фактическую очередь с помощью exchange 'amq.прямой'
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()