title: RabbitMQ Java client - How to sensibly handle exceptions and shutdowns?
body: <p>Here's what I know so far (please correct me):</p>
  
  <p>In the RabbitMQ Java client, operations on a channel throw <code>IOException</code> when there is a general network failure (malformed data from broker, authentication failures, missed heartbeats).</p>
  
  <p>Operations on a channel can also throw the <code>ShutdownSignalException</code> unchecked exception, typically an <code>AlreadyClosedException</code> when we tried to perform an action on the channel/connection after it has been shut down.</p>
  
  <p>The shutting down process happens in the event of <a href="https://www.rabbitmq.com/api-guide.html#shutdown" rel="noreferrer">"network failure, internal failure or explicit local shutdown"</a> (e.g. 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 <code>handleShutdown()</code> method gets called.</p>
  
  <p>A user can also add a shutdown listener which is called after the shutdown process completes.</p>
  
  <p><strong>Here is what I'm missing:</strong></p>
  
  <ol>
  <li>Since an IOException indicates a network failure, does it also initiate a shutdown request?</li>
  <li>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?</li>
  </ol>
  
  <p><strong>Here is how I'm handling exceptions at the moment, is this a sensible approach?</strong></p>
  
  <p>My setup is that I'm polling a QueueingConsumer and dispatching tasks to a worker pool. The rabbitmq client is encapsulated in <code>MyRabbitMQWrapper</code> 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.</p>
  
  <p>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.</p>
  
  <p>Pseudo-code:</p>
  
  <pre><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");
          }
      }
  }
  </code></pre>