class BaseWorker(object): def __init__(self, ioloop, app): self.io_loop = ioloop self.app = app self.connected = False self.connecting = False self.connection = None self.channel = None self.consumer_tag = None self.event_listeners = set([]) def connect(self): if self.connecting: return self.connecting = True param = pika.ConnectionParameters(host=QUEUE_HOST) self.connection = TornadoConnection( param, on_open_callback=self.on_connected) self.connection.add_on_close_callback(self.on_closed) def on_connected(self, connection): self.connected = True self.connection = connection self.connection.channel(self.on_channel_open) def on_channel_open(self, channel): self.channel = channel channel.queue_declare( self.on_queue_declareok, queue=QUEUE_NAME, durable=True) def on_queue_declareok(self, method_frame): self.consumer_tag = self.channel.basic_consume(self.on_message, QUEUE_NAME) def on_closed(self, connection): self.io_loop.stop() def on_message(self, channel, method, header, body): logger.info('Message received: %s' % body) self.process(channel, method, header, body) self.channel.basic_ack(delivery_tag=method.delivery_tag) def process(self, channel, method, header, body): raise NotImplementedError
class PikaClient(): def __init__(self, ioloop): self.ioloop = ioloop self.connected = False self.connecting = False self.connection = None self.channel = None self.event_listeners = set([]) def connect(self): if self.connecting: LOGGER.info('Already connected to RabbitMQ server') return LOGGER.info('Opening a connection to RabbitMQ server') self.connecting = True self.connection = TornadoConnection( parameters=settings.pika_parameters, on_open_callback=self.on_connection_open) self.connection.add_on_close_callback(self.on_closed) def on_connection_open(self, connection): LOGGER.info('Opening a connection to RabbitMQ server completed') self.connected = True self.connection = connection self.open_channel() def open_channel(self): LOGGER.info('Opening a channel') self.connection.channel(on_open_callback=self.on_channel_open) def on_channel_open(self, channel): LOGGER.info('Opening a channel completed') self.channel = channel self.declare_exchange() # def on_channel_open(self, channel): # LOGGER.info('PikaClient: Channel open, Declaring exchange') # self.channel = channel # # declare exchanges, which in turn, declare # # queues, and bind exchange to queues def declare_exchange(self): LOGGER.info('Declaring an exchange') self.channel.exchange_declare( exchange=settings.RABBITMQ_NEWSFEED_ENTRY_EXCHANGE_NAME, exchange_type=settings.RABBITMQ_NEWSFEED_ENTRY_EXCHANGE_TYPE, passive=settings.RABBITMQ_NEWSFEED_ENTRY_EXCHANGE_PASSIVE, durable=settings.RABBITMQ_NEWSFEED_ENTRY_EXCHANGE_DURABLE, auto_delete=settings.RABBITMQ_NEWSFEED_ENTRY_EXCHANGE_AUTO_DELETE, internal=settings.RABBITMQ_NEWSFEED_ENTRY_EXCHANGE_INTERNAL, nowait=settings.RABBITMQ_NEWSFEED_ENTRY_EXCHANGE_NOWAIT, arguments=None, callback=self.on_exchange_declared) def on_exchange_declared(self, unused_frame): LOGGER.info('Declaring an exchange completed') self.declare_queue() def declare_queue(self): LOGGER.info('Declaring a queue') self.channel.queue_declare( callback=self.on_queue_declared, queue=settings.RABBITMQ_NEWSFEED_ENTRY_QUEUE_NAME, passive=settings.RABBITMQ_NEWSFEED_ENTRY_QUEUE_PASSIVE, durable=settings.RABBITMQ_NEWSFEED_ENTRY_QUEUE_DURABLE, exclusive=settings.RABBITMQ_NEWSFEED_ENTRY_QUEUE_EXCLUSIVE, auto_delete=settings.RABBITMQ_NEWSFEED_ENTRY_QUEUE_AUTO_DELETE, nowait=settings.RABBITMQ_NEWSFEED_ENTRY_QUEUE_NOWAIT, arguments=None) def on_queue_declared(self, method_frame): LOGGER.info('Declaring a queue completed') self.bind_queue() def bind_queue(self): LOGGER.info('Binding a queue') self.channel.queue_bind( callback=self.on_queue_binded, queue=settings.RABBITMQ_NEWSFEED_ENTRY_QUEUE_NAME, exchange=settings.RABBITMQ_NEWSFEED_ENTRY_EXCHANGE_NAME, routing_key=settings.RABBITMQ_NEWSFEED_ENTRY_ROUTING_KEY) def on_queue_binded(self, frame): LOGGER.info('Binding a queue completed') self.channel.basic_consume( consumer_callback=self.on_message, queue=settings.RABBITMQ_NEWSFEED_ENTRY_QUEUE_NAME, no_ack= False, # Set to True means tell the broker to not expect a response exclusive= False, # Set to True means don't allow other consumers on the queue consumer_tag=None) # Specify your own consumer tag def on_closed(self, connection): LOGGER.info('Connection to RabbitMQ server closed') self.ioloop.stop() #def on_message(self, channel, method, header, body): def on_message(self, channel, basic_deliver, properties, body): LOGGER.info('Received message # %s from %s', basic_deliver.delivery_tag, properties.app_id) self.ack_message(basic_deliver.delivery_tag) self.notify_listeners(body) def ack_message(self, delivery_tag): """Acknowledge the message delivery from RabbitMQ by sending a Basic.Ack RPC method for the delivery tag. :param int delivery_tag: The delivery tag from the Basic.Deliver frame """ LOGGER.info('Acknowledging message %s', delivery_tag) self.channel.basic_ack(delivery_tag) def notify_listeners(self, event_obj): # here we assume the message the sourcing app # post to the message queue is in JSON format #event_json = json.dumps(event_obj) event_json = event_obj for listener in self.event_listeners: listener.write_message(event_json) LOGGER.info('Notified %s' % repr(listener)) def add_event_listener(self, listener): self.event_listeners.add(listener) LOGGER.info('Added listener %s' % repr(listener)) def remove_event_listener(self, listener): try: self.event_listeners.remove(listener) LOGGER.info('Removed listener %s' % repr(listener)) except KeyError: pass
class PikaConnection(object): QUEUE = 'test' EXCHANGE = '' PUBLISH_INTERVAL = 1 def __init__(self, io_loop, amqp_url): self.io_loop = io_loop self.url = amqp_url self.is_connected = False self.is_connecting = False self.connection = None self.channel = None self.listeners = set([]) def connect(self): if self.is_connecting: logger.info("PikaConnection: Already connecting to RabbitMQ") return logger.info("PikaConnection: Connecting to RabbitMQ") self.connecting = True self.connection = TornadoConnection( pika.URLParameters(self.url), on_open_callback=self.on_connected) self.connection.add_on_close_callback(self.on_closed) def on_connected(self, connection): logger.info("PikaConnection: connected to RabbitMQ") self.connected = True self.connection = connection self.connection.channel(self.on_channel_open) def on_closed(self, connection): logger.info("PikaConnection: connection closed") self.io_loop.stop() def on_channel_open(self, channel): self.channel = channel self.channel.add_on_close_callback(self.on_channel_closed) self.channel.queue_declare(queue=self.QUEUE, callback=self.on_queue_declared) def on_channel_closed(self, channel, reply_code, reply_text): self.connection.close() def on_queue_declared(self, frame): print "subscribe frame:", frame self.channel.basic_consume(self.on_message, self.QUEUE) def on_message(self, channel, method, header, body): logger.info(body) for listener in self.listeners: listener.emit(body) def add_event_listener(self, listener): self.listeners.add(listener) def publish_message(self, *args, **kwargs): if 'message' in kwargs: self.channel.basic_publish(exchange=self.EXCHANGE, routing_key=self.QUEUE, body=kwargs['message'])
class RabbitMQClient(BaseClient): """ Base RabbitMQ asynchronous adapter. """ def __init__(self, host=settings.HOST, port=settings.PORT, virtual_host=settings.VIRTUAL_HOST, username=settings.USERNAME, password=settings.PASSWORD, exchange_name='direct', exchange_type='direct'): """ Initialize RabbitMQ client with passed configuration or get parameters from django settings module. :param host: RabbitMQ host :type host: :class:`str` :param port: RabbitMQ port :type port: :class:`int` :param virtual_host: RabbitMQ virtual host :type virtual_host: :class:`str` :param username: RabbitMQ user :type username: :class:`str` :param password: RabbitMQ user's password :type password: :class:`str` :param exchange_name: Exchange name, see RabbitMQ docs for more info. :type exchange_name: :class:`str` :param exchange_type: Exchange type, see RabbitMQ docs for more info. :type exchange_type: :class:`str` """ self.host = host self.port = int(port) self.virtual_host = virtual_host self.username = username self.password = password self.exchange_name = exchange_name self.exchange_type = exchange_type self.connected = False self.connecting = False self.reconnecting = False self.closing = False self.connection = None self.channel = None ############################################################################ # CONNECTION METHODS ############################################################################ def connect(self): """ Asynchronous connection. Connects to RabbitMQ server and establish non-blocking connection. After connection is established on_connected callback will be executed which will try to reconnect if this function failed. """ if self.connecting and not self.reconnecting: return self.connecting = True credentials = PlainCredentials(self.username, self.password) param = ConnectionParameters(host=self.host, port=self.port, virtual_host=self.virtual_host, credentials=credentials) self.connection = TornadoConnection(param, on_open_callback=self.on_connected) self.connection.add_on_close_callback(self.on_close) def reconnect(self): """ Reconnect method. Basically you don't need to call this method manually. """ self.reconnecting = True self.connect() def disconnect(self): """ Disconnect method. Call this method after you end with messaging. """ self.closing = True self.connection.close() ############################################################################ # CALLBACKS ############################################################################ def on_connected(self, connection): """ Callback on connection. Reconnect if connection dropped. Otherwise it will open channel and on_channel_open callback will be executed. :param connection: RabbitMQ connection. :type connection: :class:`TornadoConnection` """ self.connected = True self.connection = connection self.connection.channel(self.on_channel_open) def on_channel_open(self, channel, callback=lambda frame: None): """ Callback on channel open. It will declare exchanges for messaging. See RabbitMQ docs for more information. :param channel: Opened channel. :type channel: :class:`Channel` :param callback: Callback that will be executed after channel open. :type callback: callable object """ self.channel = channel self.channel.exchange_declare(exchange=self.exchange_name, type=self.exchange_type, auto_delete=False, durable=True, callback=callback) def on_close(self, connection, *args, **kwargs): """ On close callback. You don't need to manually call this method. :param connection: Established connection. :type connection: :class:`TornadoConnection` :param args: Internal args :param kwargs: Internal kwargs """ self.channel = None if not self.closing: self.reconnect() self.connection.add_timeout(5, self.reconnect) else: connection.close()