web-dev-qa-db-fra.com

Comment créer un fichier d'attente différé dans RabbitMQ?

Quel est le moyen le plus simple de créer une file d'attente de retard (ou de stationnement) avec Python, Pika et RabbitMQ? J'ai vu un questions similaire, mais aucun pour Python. 

Je trouve cela une idée utile lors de la conception d'applications, car cela nous permet de limiter les messages qui doivent être re-mis en file d'attente.

Il est toujours possible que vous receviez plus de messages que vous ne puissiez en gérer, le serveur HTTP est peut-être lent ou la base de données trop sollicitée.

J'ai également trouvé cela très utile lorsque quelque chose n'allait pas dans des scénarios où la tolérance à la perte de messages était nulle, et lorsque remettre en file d'attente des messages qui ne pouvaient pas être traités pouvait résoudre ce problème. Cela peut également causer des problèmes lorsque le message sera mis en file d'attente encore et encore. Peut causer des problèmes de performances et enregistrer le spam. 

41
eandersson

J'ai trouvé cela extrêmement utile lors du développement de mes applications. Comme cela vous offre une alternative à la simple mise en attente de vos messages. Cela peut facilement réduire la complexité de votre code et constitue l'une des nombreuses fonctionnalités cachées puissantes de RabbitMQ.

Pas

Nous devons d’abord configurer deux canaux de base, un pour la file d’attente principale et un pour la file d’attente. Dans mon exemple à la fin, j'inclus quelques indicateurs supplémentaires qui ne sont pas obligatoires, mais qui rendent le code plus fiable. tels que confirm delivery, delivery_mode et durable. Vous trouverez plus d'informations à ce sujet dans le manuel RabbitMQ manual .

Après avoir configuré les canaux, nous ajoutons une liaison au canal principal que nous pouvons utiliser pour envoyer des messages du canal de délai à notre file d'attente principale.

channel.queue_bind(exchange='amq.direct',
                   queue='hello')

Ensuite, nous devons configurer notre canal de délai pour transférer les messages vers la file d'attente principale une fois qu'ils ont expiré.

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(Message - Time to Live)}

    Ceci est normalement utilisé pour supprimer automatiquement les anciens messages de la file Après une durée spécifique, mais en ajoutant deux arguments facultatifs, nous pouvons changer ce comportement et laisser ce paramètre déterminer rester dans la file d'attente.

  • x-dead-letter-routing-key

    Cette variable nous permet de transférer le message dans une file d'attente différente Une fois qu'ils ont expiré, au lieu du comportement par défaut de removeit complètement.

  • x-dead-letter-exchange

    Cette variable détermine quel Exchange utilisé pour transférer le message de hello_delay à la file d'attente hello.

Publication dans la file d'attente

Lorsque nous avons fini de configurer tous les paramètres de base du Pika, il vous suffit d'envoyer un message à la file d'attente à l'aide de la publication de base.

delay_channel.basic_publish(exchange='',
                      routing_key='hello_delay',
                      body="test",
                      properties=pika.BasicProperties(delivery_mode=2))

Une fois que vous avez exécuté le script, vous devez voir les files d’attente suivantes créées dans votre module de gestion RabbitMQ. enter image description here

Exemple.

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"
85
eandersson

Vous pouvez utiliser le plugin officiel RabbitMQ: x-delay-message.

Tout d’abord, téléchargez et copiez le fichier ez dans Your_rabbitmq_root_path/plugins

Deuxièmement, activez le plug-in (il n'est pas nécessaire de redémarrer le serveur):

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

Enfin, publiez votre message avec des en-têtes "x-delay" tels que:

headers.put("x-delay", 5000);

Remarquer:

Cela n'assure pas la sécurité de votre message, car si votre message expire juste pendant le temps d'arrêt de votre serveur rabbitmq, malheureusement, le message est perdu. Donc, soyez prudent lorsque vous utilisez ce schéma.

Profitez-en et plus d'infos dans Rabbitmq-Delay-Exchange-Message

13
flycee

FYI, comment faire cela au printemps 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>
8
Ryan Walls

Implémentation de NodeJS.

Tout est assez clair dans le code .. J'espère que cela fera gagner du temps à quelqu'un.

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});
});
2
walv

Le message dans la file d'attente Rabbit peut être retardé de 2 manières - en utilisant QUEUE TTL - using Message TTL Si tous les messages de la file d'attente doivent être différés pour une heure fixe, utilisez la file d'attente TTL . Si chaque message doit être retardé par une heure variable, utilisez Message TTL . Je l'ai expliqué avec python3 et pika module . pika L'argument BasicProperties 'expiration' en millisecondes doit être paramétré pour retarder le message dans la file d'attente à retardement . ), une fois le message de la file d'attente différée expiré, il sera acheminé vers une file d'attente réelle à l'aide de l'échange '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()
0
Kushwanth Mandadapu