Esempio n. 1
0
class BaseAMQPConnection(BaseConnection):
    """ An object which does an actual job of (re-)connecting to the the AMQP broker.
    Concrete subclasses implement either listening or publishing features.
    """
    def __init__(self, conn_params, item_name, properties=None):
        super(BaseAMQPConnection, self).__init__()
        self.conn_params = conn_params
        self.item_name = item_name
        self.properties = properties
        self.conn = None
        self.channel = None
        self.reconnect_exceptions = (TypeError, EnvironmentError)
        
    def _start(self):
        self.conn = TornadoConnection(self.conn_params, self._on_connected)
        self.conn.ioloop.start()
        
    def _close(self):
        """ Actually close the connection.
        """
        if self.conn:
            self.conn.close()
            
    def _conn_info(self):
        return '{0}:{1}{2} ({3})'.format(self.conn_params.host, 
            self.conn_params.port, self.conn_params.virtual_host, self.item_name)
            
    def _on_channel_open(self, channel):
        self.channel = channel
        msg = 'Got a channel for {0}'.format(self._conn_info())
        self.logger.debug(msg)
        
    def _keep_connecting(self, e):
        # We need to catch TypeError because pika will sometimes erroneously raise
        # it in self._start's self.conn.ioloop.start().
        # Otherwise, it may one of the network errors we are hopefully able to recover from.
        return isinstance(e, TypeError) or (isinstance(e, EnvironmentError) 
                                            and e.errno in self.reconnect_error_numbers)
            
    def _on_connected(self, conn):
        """ Invoked after establishing a successful connection to an AMQP broker.
        Will report a diagnostic message regarding how many attempts there were
        and how long it took if the connection hasn't been established straightaway.
        """
        super(BaseAMQPConnection, self)._on_connected()
        conn.channel(self._on_channel_open)
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. 3
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()