class BrokerConnection(object): """Connection class which provides connection to AMQP broker.""" def __init__(self, config, callback): self.reconnection_delay = 1.0 self.caller_callback = callback self.config = ConnectionConfig() self.config.read(config) self.callbacks = self.set_callbacks() credentials = PlainCredentials(**self.config.credentials) broker_config = self.config.broker_config broker_config['credentials'] = credentials parameters = ConnectionParameters(**broker_config) try: parameters.host = self.config.host except NoOptionError: pass try: parameters.heartbeat = int(self.config.heartbeat) except NoOptionError: pass try: parameters.heartbeat = int(self.config.heartbeat) except NoOptionError: pass self.connection = SelectConnection(parameters, self.on_connected) def on_connected(self, connection): """Called when we're connected to AMQP broker.""" logger.info("Connected to AMQP-broker.") connection.channel(self.on_channel_open) def on_channel_open(self, new_channel): """Called when channel has opened""" logger.info("Channel to AMQP-broker opened.") self.channel = new_channel self.channel.add_on_close_callback(self.on_channel_closed) self.generic_callback() def set_callbacks(self): """Set callbacks for queue factory.""" self.exchange_callbacks = [] self.queue_callbacks = [] self.binding_callbacks = [] for exchange in self.config.exchanges: tmp = {} tmp['exchange'] = exchange tmp.update(self.config.exchanges[exchange]) self.exchange_callbacks.append(tmp) for queue in self.config.queues: tmp = {} tmp['queue'] = queue tmp.update(self.config.queues[queue]) self.queue_callbacks.append(tmp) for binding in self.config.bindings: tmp = {} tmp['binding'] = binding tmp.update(self.config.bindings[binding]) self.binding_callbacks.append(tmp) def generic_callback(self, channel=None, frame=None): """Create exchanges, queues and bindings.""" if channel and frame: # You could do error handling here # or add code for anonymous queue handling pass if self.exchange_callbacks: config = self.exchange_callbacks.pop() config['exchange'] = config['exchange'].split(':', 1)[-1] logger.info("Creating exchange %s." % config['exchange']) config.update({"callback": self.generic_callback}) self.channel.exchange_declare(**config) elif self.queue_callbacks: config = self.queue_callbacks.pop() config['queue'] = config['queue'].split(':', 1)[-1] logger.info("Creating queue %s." % config['queue']) config.update({"callback": self.generic_callback}) self.channel.queue_declare(**config) elif self.binding_callbacks: config = self.binding_callbacks.pop() binding = config['binding'].split(':') del config['binding'] config['exchange'] = binding[-1] config['queue'] = binding[1] logger.info("Creating binding (%s -> %s) with routing key %s" % ( config['exchange'], config['queue'], config['routing_key'])) config.update({"callback": self.generic_callback}) self.channel.queue_bind(**config) else: logger.info("RabbitMQ exchanges, queues and bindings are now " "configured.") self.caller_callback() def reset_reconnection_delay(self, *args): self.reconnection_delay = 1.0 def loop(self): """Main loop""" while True: try: logger.info("Starting main loop") self.connection.add_on_open_callback( self.reset_reconnection_delay) self.connection.ioloop.start() except socket.error as e: logger.error("Connection failed or closed unexpectedly: %s", e) except TypeError as e: logger.error("Connection failed or closed unexpectedly: %s", e) except KeyboardInterrupt: self.connection.close() self.connection.ioloop.start() break finally: self.reconnection_delay *= (random.random() * 0.5) + 1 self.reconnection_delay = min(self.reconnection_delay, 60.0) logger.info("Trying reconnection in %s seconds", self.reconnection_delay) time.sleep(self.reconnection_delay) def on_channel_closed(self, channel, code, text): logger.warning("Channel closed with reason '%s %s'", code, text) self.connection.close(code, text)