All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rabbitmq.client.impl.recovery.AutorecoveringConnection Maven / Gradle / Ivy

package com.rabbitmq.client.impl.recovery;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Address;
import com.rabbitmq.client.BlockedListener;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Recoverable;
import com.rabbitmq.client.RecoveryListener;
import com.rabbitmq.client.ShutdownListener;
import com.rabbitmq.client.ShutdownSignalException;
import com.rabbitmq.client.TopologyRecoveryException;
import com.rabbitmq.client.impl.ConnectionParams;
import com.rabbitmq.client.ExceptionHandler;
import com.rabbitmq.client.impl.FrameHandlerFactory;
import com.rabbitmq.client.impl.NetworkConnection;

import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Connection implementation that performs automatic recovery when
 * connection shutdown is not initiated by the application (e.g. due to
 * an I/O exception).
 *
 * Topology (exchanges, queues, bindings, and consumers) can be (and by default is) recovered
 * as well, in this order:
 *
 * 
    *
  1. Exchanges
  2. *
  3. Queues
  4. *
  5. Bindings (both queue and exchange-to-exchange)
  6. *
  7. Consumers
  8. *
* * @see com.rabbitmq.client.Connection * @see com.rabbitmq.client.Recoverable * @see com.rabbitmq.client.ConnectionFactory#setAutomaticRecovery(boolean) * @see com.rabbitmq.client.ConnectionFactory#setTopologyRecovery(boolean) * @since 3.3.0 */ public class AutorecoveringConnection implements Connection, Recoverable, NetworkConnection { private final RecoveryAwareAMQConnectionFactory cf; private final Map channels; private final ConnectionParams params; private RecoveryAwareAMQConnection delegate; private final List shutdownHooks = new ArrayList(); private final List recoveryListeners = new ArrayList(); private final List blockedListeners = new ArrayList(); // Records topology changes private final Map recordedQueues = new ConcurrentHashMap(); private final List recordedBindings = new ArrayList(); private Map recordedExchanges = new ConcurrentHashMap(); private final Map consumers = new ConcurrentHashMap(); public AutorecoveringConnection(ConnectionParams params, FrameHandlerFactory f, Address[] addrs) { this.cf = new RecoveryAwareAMQConnectionFactory(params, f, addrs); this.params = params; this.channels = new ConcurrentHashMap(); } /** * Private API. * @throws IOException * @see com.rabbitmq.client.ConnectionFactory#newConnection(java.util.concurrent.ExecutorService) */ public void init() throws IOException { this.delegate = this.cf.newConnection(); this.addAutomaticRecoveryListener(); } public void start() throws IOException { // no-op, AMQConnection#start is executed in ConnectionFactory#newConnection // and invoking it again will result in a framing error. MK. } /** * @see com.rabbitmq.client.Connection#createChannel() */ public Channel createChannel() throws IOException { RecoveryAwareChannelN ch = (RecoveryAwareChannelN) delegate.createChannel(); if (ch == null) { return null; } else { return this.wrapChannel(ch); } } /** * @see com.rabbitmq.client.Connection#createChannel(int) */ public Channel createChannel(int channelNumber) throws IOException { return delegate.createChannel(channelNumber); } /** * Creates a recovering channel from a regular channel and registers it. * If the regular channel cannot be created (e.g. too many channels are open * already), returns null. * * @param delegateChannel Channel to wrap. * @return Recovering channel. */ private Channel wrapChannel(RecoveryAwareChannelN delegateChannel) { final AutorecoveringChannel channel = new AutorecoveringChannel(this, delegateChannel); if (delegateChannel == null) { return null; } else { this.registerChannel(channel); return channel; } } void registerChannel(AutorecoveringChannel channel) { this.channels.put(channel.getChannelNumber(), channel); } void unregisterChannel(AutorecoveringChannel channel) { this.channels.remove(channel.getChannelNumber()); } /** * @see com.rabbitmq.client.Connection#getServerProperties() */ public Map getServerProperties() { return delegate.getServerProperties(); } /** * @see com.rabbitmq.client.Connection#getClientProperties() */ public Map getClientProperties() { return delegate.getClientProperties(); } /** * @see com.rabbitmq.client.Connection#getFrameMax() */ public int getFrameMax() { return delegate.getFrameMax(); } /** * @see com.rabbitmq.client.Connection#getHeartbeat() */ public int getHeartbeat() { return delegate.getHeartbeat(); } /** * @see com.rabbitmq.client.Connection#getChannelMax() */ public int getChannelMax() { return delegate.getChannelMax(); } /** * @see com.rabbitmq.client.Connection#isOpen() */ public boolean isOpen() { return delegate.isOpen(); } /** * @see com.rabbitmq.client.Connection#close() */ public void close() throws IOException { delegate.close(); } /** * @see Connection#close(int) */ public void close(int timeout) throws IOException { delegate.close(timeout); } /** * @see Connection#close(int, String, int) */ public void close(int closeCode, String closeMessage, int timeout) throws IOException { delegate.close(closeCode, closeMessage, timeout); } /** * @see com.rabbitmq.client.Connection#abort() */ public void abort() { delegate.abort(); } /** * @see Connection#abort(int, String, int) */ public void abort(int closeCode, String closeMessage, int timeout) { delegate.abort(closeCode, closeMessage, timeout); } /** * @see Connection#abort(int, String) */ public void abort(int closeCode, String closeMessage) { delegate.abort(closeCode, closeMessage); } /** * @see Connection#abort(int) */ public void abort(int timeout) { delegate.abort(timeout); } /** * @see com.rabbitmq.client.Connection#getCloseReason() */ public ShutdownSignalException getCloseReason() { return delegate.getCloseReason(); } /** * @see com.rabbitmq.client.ShutdownNotifier#addShutdownListener(com.rabbitmq.client.ShutdownListener) */ public void addBlockedListener(BlockedListener listener) { this.blockedListeners.add(listener); delegate.addBlockedListener(listener); } /** * @see Connection#removeBlockedListener(com.rabbitmq.client.BlockedListener) */ public boolean removeBlockedListener(BlockedListener listener) { this.blockedListeners.remove(listener); return delegate.removeBlockedListener(listener); } /** * @see com.rabbitmq.client.Connection#clearBlockedListeners() */ public void clearBlockedListeners() { this.blockedListeners.clear(); delegate.clearBlockedListeners(); } /** * @see com.rabbitmq.client.Connection#close(int, String) */ public void close(int closeCode, String closeMessage) throws IOException { delegate.close(closeCode, closeMessage); } /** * @see Connection#addShutdownListener(com.rabbitmq.client.ShutdownListener) */ public void addShutdownListener(ShutdownListener listener) { this.shutdownHooks.add(listener); delegate.addShutdownListener(listener); } /** * @see com.rabbitmq.client.ShutdownNotifier#removeShutdownListener(com.rabbitmq.client.ShutdownListener) */ public void removeShutdownListener(ShutdownListener listener) { this.shutdownHooks.remove(listener); delegate.removeShutdownListener(listener); } /** * @see com.rabbitmq.client.ShutdownNotifier#notifyListeners() */ public void notifyListeners() { delegate.notifyListeners(); } /** * Adds the recovery listener * @param listener {@link com.rabbitmq.client.RecoveryListener} to execute after this connection recovers from network failure */ public void addRecoveryListener(RecoveryListener listener) { this.recoveryListeners.add(listener); } /** * Removes the recovery listener * @param listener {@link com.rabbitmq.client.RecoveryListener} to remove */ public void removeRecoveryListener(RecoveryListener listener) { this.recoveryListeners.remove(listener); } /** * @see com.rabbitmq.client.impl.AMQConnection#getExceptionHandler() */ @SuppressWarnings("unused") public ExceptionHandler getExceptionHandler() { return this.delegate.getExceptionHandler(); } /** * @see com.rabbitmq.client.Connection#getPort() */ public int getPort() { return delegate.getPort(); } /** * @see com.rabbitmq.client.Connection#getAddress() */ public InetAddress getAddress() { return delegate.getAddress(); } /** * @return client socket address */ public InetAddress getLocalAddress() { return this.delegate.getLocalAddress(); } /** * @return client socket port */ public int getLocalPort() { return this.delegate.getLocalPort(); } // // Recovery // private void addAutomaticRecoveryListener() { final AutorecoveringConnection c = this; ShutdownListener automaticRecoveryListener = new ShutdownListener() { public void shutdownCompleted(ShutdownSignalException cause) { try { if (!cause.isInitiatedByApplication()) { c.beginAutomaticRecovery(); } } catch (Exception e) { c.delegate.getExceptionHandler().handleConnectionRecoveryException(c, e); } } }; synchronized (this) { this.shutdownHooks.add(automaticRecoveryListener); this.delegate.addShutdownListener(automaticRecoveryListener); } } synchronized private void beginAutomaticRecovery() throws InterruptedException, IOException, TopologyRecoveryException { Thread.sleep(this.params.getNetworkRecoveryInterval()); this.recoverConnection(); this.recoverShutdownListeners(); this.recoverBlockedListeners(); this.recoverChannels(); if(this.params.isTopologyRecoveryEnabled()) { this.recoverEntities(); this.recoverConsumers(); } this.notifyRecoveryListeners(); } private void recoverShutdownListeners() { for (ShutdownListener sh : this.shutdownHooks) { this.delegate.addShutdownListener(sh); } } private void recoverBlockedListeners() { for (BlockedListener bl : this.blockedListeners) { this.delegate.addBlockedListener(bl); } } private void recoverConnection() throws IOException, InterruptedException { boolean recovering = true; while (recovering) { try { this.delegate = this.cf.newConnection(); recovering = false; } catch (ConnectException ce) { // TODO: exponential back-off Thread.sleep(this.params.getNetworkRecoveryInterval()); this.getExceptionHandler().handleConnectionRecoveryException(this, ce); } catch (Exception e) { this.getExceptionHandler().handleConnectionRecoveryException(this, e); } } } private void recoverChannels() { for (AutorecoveringChannel ch : this.channels.values()) { try { ch.automaticallyRecover(this, this.delegate); } catch (Throwable t) { this.delegate.getExceptionHandler().handleChannelRecoveryException(ch, t); } } } private void notifyRecoveryListeners() { for (RecoveryListener f : this.recoveryListeners) { f.handleRecovery(this); } } private void recoverEntities() throws TopologyRecoveryException { // The recovery sequence is the following: // // 1. Recover exchanges // 2. Recover queues // 3. Recover bindings // 4. Recover consumers recoverExchanges(); recoverQueues(); recoverBindings(); } private void recoverExchanges() { // recorded exchanges are guaranteed to be // non-predefined (we filter out predefined ones // in exchangeDeclare). MK. for (RecordedExchange x : this.recordedExchanges.values()) { try { x.recover(); } catch (Exception cause) { TopologyRecoveryException e = new TopologyRecoveryException("Caught an exception while recovering exchange " + x.getName(), cause); this.getExceptionHandler().handleTopologyRecoveryException(delegate, x.getDelegateChannel(), e); } } } private void recoverQueues() { for (Map.Entry entry : this.recordedQueues.entrySet()) { String oldName = entry.getKey(); RecordedQueue q = entry.getValue(); try { q.recover(); String newName = q.getName(); // make sure server-named queues are re-added with // their new names. MK. synchronized (this.recordedQueues) { deleteRecordedQueue(oldName); this.recordedQueues.put(newName, q); this.propagateQueueNameChangeToBindings(oldName, newName); this.propagateQueueNameChangeToConsumers(oldName, newName); } } catch (Exception cause) { TopologyRecoveryException e = new TopologyRecoveryException("Caught an exception while recovering queue " + oldName, cause); this.getExceptionHandler().handleTopologyRecoveryException(delegate, q.getDelegateChannel(), e); } } } private void recoverBindings() { for (RecordedBinding b : this.recordedBindings) { try { b.recover(); } catch (Exception cause) { String message = "Caught an exception while recovering binding between " + b.getSource() + " and " + b.getDestination(); TopologyRecoveryException e = new TopologyRecoveryException(message, cause); this.getExceptionHandler().handleTopologyRecoveryException(delegate, b.getDelegateChannel(), e); } } } private void recoverConsumers() { for (Map.Entry entry : this.consumers.entrySet()) { String tag = entry.getKey(); RecordedConsumer consumer = entry.getValue(); try { String newTag = consumer.recover(); // make sure server-generated tags are re-added. MK. synchronized (this.consumers) { this.consumers.remove(tag); this.consumers.put(newTag, consumer); } } catch (Exception cause) { TopologyRecoveryException e = new TopologyRecoveryException("Caught an exception while recovering consumer " + tag, cause); this.getExceptionHandler().handleTopologyRecoveryException(delegate, consumer.getDelegateChannel(), e); } } } private void propagateQueueNameChangeToBindings(String oldName, String newName) { for (RecordedBinding b : this.recordedBindings) { if (b.getDestination().equals(oldName)) { b.setDestination(newName); } } } private void propagateQueueNameChangeToConsumers(String oldName, String newName) { for (RecordedConsumer c : this.consumers.values()) { if (c.getQueue().equals(oldName)) { c.setQueue(newName); } } } synchronized void recordQueueBinding(AutorecoveringChannel ch, String queue, String exchange, String routingKey, Map arguments) { RecordedBinding binding = new RecordedQueueBinding(ch). source(exchange). destination(queue). routingKey(routingKey). arguments(arguments); if (!this.recordedBindings.contains(binding)) { this.recordedBindings.add(binding); } } synchronized boolean deleteRecordedQueueBinding(AutorecoveringChannel ch, String queue, String exchange, String routingKey, Map arguments) { RecordedBinding b = new RecordedQueueBinding(ch). source(exchange). destination(queue). routingKey(routingKey). arguments(arguments); return this.recordedBindings.remove(b); } synchronized void recordExchangeBinding(AutorecoveringChannel ch, String destination, String source, String routingKey, Map arguments) { RecordedBinding binding = new RecordedExchangeBinding(ch). source(source). destination(destination). routingKey(routingKey). arguments(arguments); this.recordedBindings.add(binding); } synchronized boolean deleteRecordedExchangeBinding(AutorecoveringChannel ch, String destination, String source, String routingKey, Map arguments) { RecordedBinding b = new RecordedExchangeBinding(ch). source(source). destination(destination). routingKey(routingKey). arguments(arguments); return this.recordedBindings.remove(b); } void recordQueue(AMQP.Queue.DeclareOk ok, RecordedQueue q) { this.recordedQueues.put(ok.getQueue(), q); } void deleteRecordedQueue(String queue) { this.recordedQueues.remove(queue); } void recordExchange(String exchange, RecordedExchange x) { this.recordedExchanges.put(exchange, x); } void deleteRecordedExchange(String exchange) { this.recordedExchanges.remove(exchange); } void recordConsumer(String result, RecordedConsumer consumer) { this.consumers.put(result, consumer); } void deleteRecordedConsumer(String consumerTag) { this.consumers.remove(consumerTag); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy