
# 实现RabbitMQ节点重启后消费者自动重连
#### 方案概述
RabbitMQ的amqp-client虽然自带重连机制，但是自带的重连机制只会重试一次，重连失败后就不再执行。这时如果消费者没有做额外的重试机制，那么这个消费者就彻底断开与服务端的连接，无法消费消息。
amqp-client在节点断连后，根据与通道建立的节点不同，产生不同的错误。
- 如果通道连接的是队列所在的节点，消费者就会收到一个shutdown信号。这时amqp-client的重连机制就会生效，尝试重新连接服务端。如果连接成功，这个通道就会继续连接消费。如果连接失败，就会执行**channel.close**方法，关闭这个通道。
- 如果通道连接的不是队列所在的节点，消费者不会触发关闭动作，而是由服务端发送的一个取消动作。这个动作对amqp-client来说并不是异常行为，所以日志上不会有明显的报错，但是连接最终还是会关闭。
amqp-client出现上面两种错误时，会分别回调**handleShutdownSignal** 以及**handleCancel**方法。您可以通过重写这两种方法，在回调时执行重写的重连逻辑，就能在通道关闭后重新为消费者创建新的通道继续消费。
#### RabbitMQ节点重启后消费者自动重连示例代码
以下提供一个简单的Java代码示例，此示例能够解决上面的两种错误，实现消费者的持续消费。
```
package rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;
public class RabbitConsumer {
    public static void main(String... args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        // 配置实例的连接地址和端口
        factory.setHost("192.168.0.2");
        factory.setPort(5672);
        // 配置实例连接的用户名和密码
        factory.setUsername("name");
        factory.setPassword("password");
        Connection connection = factory.newConnection();
        createNewConnection(connection);
    }
    // 重连处理
    public static void createNewConnection(Connection connection) {
        try {
            Thread.sleep(1000);
            Channel channel = connection.createChannel();
            channel.basicQos(64);
            channel.basicConsume("queue-01", false, new CustomConsumer(channel, connection));
        } catch (Exception e) {
//            e.printStackTrace();
            createNewConnection(connection);
        }
    }
    static class CustomConsumer implements Consumer {
        private final Channel _channel;
        private final Connection _connection;
        public CustomConsumer(Channel channel, Connection connection) {
            _channel = channel;
            _connection = connection;
        }
        @Override
        public void handleConsumeOk(String consumerTag) {
        }
        @Override
        public void handleCancelOk(String consumerTag) {
        }
        @Override
        public void handleCancel(String consumerTag) throws IOException {
            System.out.println("handleCancel");
            System.out.println(consumerTag);
            createNewConnection(_connection);
        }
        @Override
        public void handleShutdownSignal(String consumerTag, ShutdownSignalException sig) {
            System.out.println("handleShutdownSignal");
            System.out.println(consumerTag);
            System.out.println(sig.getReason());
            createNewConnection(_connection);
        }
        @Override
        public void handleRecoverOk(String consumerTag) {
        }
        @Override
        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
            String message = new String(body, StandardCharsets.UTF_8);
            System.out.println(" [x] Received '" + message + "'");
            _channel.basicAck(envelope.getDeliveryTag(), false);
        }
    }
}
```
