Esempio n. 1
0
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
Esempio n. 2
0
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'])
Esempio n. 4
0
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()