在消费者重新连接之前,RabbitMQ会丢失消息

时间:2013-08-09 09:49:34

标签: rabbitmq amqp

我实现了一个使用者,如果基础连接关闭,它会在一段时间后自动重新连接到代理。我的情况如下:

  1. 成功启动RabbitMQ服务器。
  2. 成功推出消费者。
  3. 发布了一条消息,消费者已成功收到消息。
  4. 停止RabbitMQ服务器,消费者将显示异常:

      

    com.rabbitmq.client.ShutdownSignalException:连接错误;原因:{#method(reply-code = 541,reply-text = INTERNAL_ERROR,class-id = 0,method-id = 0),null,“”}。

    然后消费者将在重新连接前休息60秒。

  5. 再次启动RabbitMQ服务器。
  6. 成功发布消息,命令'list_queues'的结果为0
  7. 60秒后,消费者再次连接到RabbitMQ,但现在收到的消息已在步骤#6中发布。
  8. 发布第3条消息,消费者已成功收到。
  9. 在这种情况下,重新连接之前发布的所有消息都将丢失。 我还做了另一个实验。

    1. 启动RabbitMQ,并成功发布消息(未启动任何消费者进程)。
    2. 停止RabbitMQ,然后重新启动它。
    3. 启动消费者流程,成功接收步骤#1发布的消息。
    4. 注意:消费者的QOS是1。 我已经研究过RabbitMQ好几天,根据我的理解,消费者应该在重新连接之前发布消息。 请帮忙(我根据windows rabbitMQ运行测试)。

      以下是PUBLISHER:

      ConnectionFactory factory = new ConnectionFactory();
      factory.setHost(this.getHost());
      connection = factory.newConnection();
      Channel channel = connection.createChannel();               
      channel = conn.createChannel();
      // declare a 'topic' type of exchange
      channel.exchangeDeclare(exchangeName, "topic");
      // Content-type "application/octet-stream", deliveryMode 2
      // (persistent), priority zero
      channel.basicPublish(exchangeName, routingKey, MessageProperties.PERSISTENT_BASIC, message);
      connection.close();
      

      消费者如下:

          @Override
      public void consume(final String exchangeName, final String queueName, final String routingKey,
              final int qos) throws IOException, InterruptedException {
          ConnectionFactory factory = new ConnectionFactory();
          factory.setHost(this.getHost());
      
          while (true) {
              Connection connection = null;
              try {
                  connection = factory.newConnection();
                  Channel channel = connection.createChannel();
      
                  channel.exchangeDeclare(exchangeName, "topic");
                  // declare a durable, non-exclusive, non-autodelete queue.
                  channel.queueDeclare(queueName, true, false, false, null);
                  channel.queueBind(queueName, exchangeName, routingKey);
                  // distribute workload among all consumers, consumer will
                  // pre-fetch
                  // {qos}
                  // messages to local buffer.
                  channel.basicQos(qos);
      
                  logger.debug(" [*] Waiting for messages. To exit press CTRL+C");
      
                  QueueingConsumer consumer = new QueueingConsumer(channel);
                  // disable auto-ack. If enable auto-ack, RabbitMQ delivers a
                  // message to
                  // the customer it immediately removes it from memory.
                  boolean autoAck = false;
                  channel.basicConsume(queueName, autoAck, consumer);
      
                  while (true) {
                      QueueingConsumer.Delivery delivery = consumer.nextDelivery();
                      try {
                          RabbitMessageConsumer.this.consumeMessage(delivery);
                      }
                      catch (Exception e) {
                          // the exception shouldn't affect the next message
                          logger.info("[IGNORE]" + e.getMessage());
                      }
                      channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
                  }
              }
              catch (Exception e) {
                  logger.warn(e);
              }
      
              if (autoReconnect) {
                  this.releaseConn(connection);
                  logger.info("[*] Will try to reconnect to remote host(" + this.getHost() + ") in "
                          + this.reconnectInterval / 1000 + " seconds.");
                  Thread.sleep(this.getReconnectInterval());
              }
              else
                  break;
          }
      }
      
      private void releaseConn(Connection conn) {
          try {
              if (conn != null)
                  conn.close();
          }
          catch (Exception e) {
              // simply ignore this exception
          }
      }
      

      由于它是一个'主题'交换,因此在PUBLISHER中没有声明队列。但是,在第一次测试的第3步,已经声明了持久队列,并且消息也是持久的。我不明白为什么在重新连接之前消息会丢失。

4 个答案:

答案 0 :(得分:8)

哦,我找到了原因......消息和队列肯定是持久的,但交换不耐用。由于交换不持久,RabbitMQ代理重启之间队列和交换之间的绑定信息将丢失。

现在我声明交换是持久的,消费者可以获得在消费者重启之前和经纪人重启之后发布的消息。

答案 1 :(得分:6)

如果在发布时没有队列,则消息将丢失。您是否连接到相同的队列,如果它是持久的,或者您在重新启动RMQ服务器之后发布消息之前重新创建它?听起来像解决方案之一:

  1. 使队列持久并在重新启动后重新连接到该队列
  2. 确保在发布之前创建队列。
  3. 此外,请确保重新连接到消费者中的正确队列。 1)可能是两种解决方案中较好的一种。

答案 2 :(得分:3)

代替channel.queueDeclare(QUEUE_NAME, true, false, false, null);

使用此channel.queueDeclare(QUEUE_NAME, false, false, false, null);

Rabbitmq-java tutorial

答案 3 :(得分:0)

从3.1升级到3.3后,我的RabbitMQ群集就会发生这种情况。我的解决方案是删除/var/lib/rabbitmq/mnesia目录。

相关问题