简体   繁体   中英

RabbitMQ Java client - How to sensibly handle exceptions and shutdowns?

Here's what I know so far (please correct me):

In the RabbitMQ Java client, operations on a channel throw IOException when there is a general network failure (malformed data from broker, authentication failures, missed heartbeats).

Operations on a channel can also throw the ShutdownSignalException unchecked exception, typically an AlreadyClosedException when we tried to perform an action on the channel/connection after it has been shut down.

The shutting down process happens in the event of "network failure, internal failure or explicit local shutdown" (eg via channel.close() or connection.close()). The shutdown event propagates down the "topology", from Connection -> Channel -> Consumer, and when the Channel it calls the Consumer's handleShutdown() method gets called.

A user can also add a shutdown listener which is called after the shutdown process completes.

Here is what I'm missing:

  1. Since an IOException indicates a network failure, does it also initiate a shutdown request?
  2. How does using auto-recovery mode affect shutdown requests? Does it cause channel operations to block while it tries to reconnect to the channel, or will the ShutdownSignalException still be thrown?

Here is how I'm handling exceptions at the moment, is this a sensible approach?

My setup is that I'm polling a QueueingConsumer and dispatching tasks to a worker pool. The rabbitmq client is encapsulated in MyRabbitMQWrapper here. When an exception occurs polling the queue I just gracefully shutdown everything and restart the client. When an exception occurs in the worker I also just log it and finish the worker.

My biggest worry (related to Question 1): Suppose an IOException occurs in the worker, then the task doesn't get acked. If the shutdown does not then occur, I now have an un-acked task that will be in limbo forever.

Pseudo-code:

class Main {
    public static void main(String[] args) {
        while(true) {
            run();
            //Easy way to restart the client, the connection has been
            //closed so RabbitMQ will re-queue any un-acked tasks.
            log.info("Shutdown occurred, restarting in 5 seconds");
            Thread.sleep(5000);
        }
    }

    public void run() {
      MyRabbitMQWrapper rw = new MyRabbitMQWrapper("localhost");

      try {
        rw.connect();

        while(!Thread.currentThread().isInterrupted()) {
           try {
               //Wait for a message on the QueueingConsumer
               MyMessage t = rw.getNextMessage();
               workerPool.submit(new MyTaskRunnable(rw, t));
           } catch (InterruptedException | IOException | ShutdownSignalException e) {
               //Handle all AMQP library exceptions by cleaning up and returning
               log.warn("Shutting down", e);
               workerPool.shutdown();
               break;
           }
        }
      } catch (IOException e) {
        log.error("Could not connect to broker", e);
      } finally {
        try { 
            rw.close(); 
        } catch(IOException e) { 
            log.info("Could not close connection");
        }
      }
    }
}

class MyTaskRunnable implements Runnable {
    ....

    public void run() {
        doStuff();
        try {
            rw.ack(...);
        } catch (IOException | ShutdownSignalException e) {
            log.warn("Could not ack task");
        }
    }
}

您可以查看Lyra是否从意外的连接/通道/消费者关闭中自动恢复。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM