Beispiel #1
0
    def __init__(self, connection: Connection, configuration: BrightsideConsumerConfiguration, logger: logging.Logger=None) -> None:
        self._exchange = Exchange(connection.exchange, type=connection.exchange_type, durable=connection.is_durable)
        self._routing_key = configuration.routing_key
        self._amqp_uri = connection.amqp_uri
        self._queue_name = configuration.queue_name
        self._routing_key = configuration.routing_key
        self._prefetch_count = configuration.prefetch_count
        self._is_durable = configuration.is_durable
        self._heartbeat = connection.heartbeat
        self._connect_timeout = connection.connect_timeout
        self._message_factory = ArameMessageFactory()
        self._logger = logger or logging.getLogger(__name__)
        self._conn = None
        consumer_arguments = {}
        if configuration.is_ha is True:
            consumer_arguments = {"x-ha-policy": "all"}

        self._is_long_running_handler = configuration.is_long_runing_handler

        self._queue = Queue(self._queue_name, exchange=self._exchange, routing_key=self._routing_key,
                            durable=self._is_durable, consumer_arguments=consumer_arguments)

        self._msg = None  # Kombu Message
        self._message = None  # Brightside Message

        self._establish_connection(BrokerConnection(hostname=self._amqp_uri, connect_timeout=self._connect_timeout, heartbeat=self._heartbeat))
        self._establish_channel()
        self._establish_consumer()
Beispiel #2
0
    def __init__(self, brightsideConnection: BrightsideConnection,
                 brightsideConfiguration: BrightsideConsumerConfiguration,
                 exchange: Exchange, broker_connection: BrokerConnection,
                 queue: Queue, logger):

        # we run ArameConsumer constructor logic explicitly here,
        # since ArameConsumer.__init__ doesn't get invoked (as its mixed in)
        self.brightsideConnection = brightsideConnection
        self._exchange = exchange
        self._routing_key = brightsideConfiguration.routing_key
        self._amqp_uri = brightsideConnection.amqp_uri
        self._queue_name = brightsideConfiguration.queue_name
        self._routing_key = brightsideConfiguration.routing_key
        self._prefetch_count = brightsideConfiguration.prefetch_count
        self._is_durable = brightsideConfiguration.is_durable
        self._message_factory = ArameMessageFactory()
        self._logger = logger or logging.getLogger(__name__)
        self._conn = None
        self._is_long_running_handler = brightsideConfiguration.is_long_runing_handler

        self._queue = queue
        self._msg = None  # Kombu Message
        self._message = None  # Brightside Message

        self.queue = queue
        self.logger = logger
        self.queues = [self.queue]
        self.messageQueue = MessageQueue()

        self.connection = broker_connection  # sets ConsumerMixin.connection !
        self._establish_connection(self.connection)  # a Kombu Connection
        self._establish_channel()
        self._establish_consumer()

        Thread(target=self.run_tasks).start()
Beispiel #3
0
 def __init__(self,
              connection: Connection,
              configuration: BrightsideConsumerConfiguration,
              logger: logging.Logger = None) -> None:
     self._exchange = Exchange(connection.exchange,
                               type=connection.exchange_type,
                               durable=connection.is_durable)
     self._routing_key = configuration.routing_key
     self._amqp_uri = connection.amqp_uri
     self._queue_name = configuration.queue_name
     self._routing_key = configuration.routing_key
     self._prefetch_count = configuration.prefetch_count
     self._is_durable = configuration.is_durable
     self._message_factory = ArameMessageFactory()
     self._logger = logger or logging.getLogger(__name__)
     self._queue = Queue(self._queue_name,
                         exchange=self._exchange,
                         routing_key=self._routing_key,
                         durable=self._is_durable)
     self._msg = None  # Kombu Message
     self._message = None  # Brightside Message
Beispiel #4
0
 def __init__(self, connection: ArameConnection, queue_name: str, routing_key: str, prefetch_count: int=1,
              is_durable: bool=False, logger: logging.Logger=None) -> None:
     self._exchange = Exchange(connection.exchange, type=connection.exchange_type, durable=connection.is_durable)
     self._routing_key = routing_key
     self._amqp_uri = connection.amqp_uri
     self._queue_name = queue_name
     self._routing_key = routing_key
     self._prefetch_count = prefetch_count
     self._is_durable = is_durable
     self._message_factory = ArameMessageFactory()
     self._logger = logger or logging.getLogger(__name__)
     self._queue = Queue(self._queue_name, exchange=self._exchange, routing_key=self._routing_key)
     self._msg = None  # Kombu Message
     self._message = None # Brightside Message
Beispiel #5
0
class ArameConsumer(BrightsideConsumer):
    """ Implements reading a message from an RMQ broker. It uses a queue, created by subscribing to a message topic

    """
    RETRY_OPTIONS = {
        'interval_start': 1,
        'interval_step': 1,
        'interval_max': 1,
        'max_retries': 3,
    }

    def __init__(self, connection: ArameConnection, queue_name: str, routing_key: str, prefetch_count: int=1,
                 is_durable: bool=False, logger: logging.Logger=None) -> None:
        self._exchange = Exchange(connection.exchange, type=connection.exchange_type, durable=connection.is_durable)
        self._routing_key = routing_key
        self._amqp_uri = connection.amqp_uri
        self._queue_name = queue_name
        self._routing_key = routing_key
        self._prefetch_count = prefetch_count
        self._is_durable = is_durable
        self._message_factory = ArameMessageFactory()
        self._logger = logger or logging.getLogger(__name__)
        self._queue = Queue(self._queue_name, exchange=self._exchange, routing_key=self._routing_key)
        self._msg = None  # Kombu Message
        self._message = None # Brightside Message

        # TODO: Need to fix the argument types with default types issue

    def acknowledge(self, message: BrightsideMessage):
        if (self._message is not None) and self._message.id == message.id:
            self._msg.ack()
            self._msg = None

    def has_acknowledged(self, message):
        if (self._message is not None) and self._message.id == message.id:
            if self._msg is None:
                return True
            else:
                return False

    def purge(self, timeout: int = 5) -> None:

        def _purge_errors(exc, interval):
            self._logger.error('Purging error: %s, will retry triggering in %s seconds', exc, interval, exc_info=True)

        def _purge_messages(cnsmr: BrightsideConsumer):
            cnsmr.purge()
            self._message = None

        connection = BrokerConnection(hostname=self._amqp_uri)
        with connections[connection].acquire(block=True) as conn:
            self._logger.debug('Got connection: %s', conn.as_uri())
            with Consumer([self._queue], callbacks=[_purge_messages]) as consumer:
                ensure_kwargs = self.RETRY_OPTIONS.copy()
                ensure_kwargs['errback'] = _purge_errors
                safe_purge = conn.ensure(consumer, _purge_messages, **ensure_kwargs)
                safe_purge(consumer)

    def receive(self, timeout: int) -> BrightsideMessage:

        self._message = BrightsideMessage(BrightsideMessageHeader(uuid4(), "", BrightsideMessageType.none), BrightsideMessageBody(""))

        def _consume(cnx: BrokerConnection, timesup: int) -> None:
            try:
                cnx.drain_events(timeout=timesup)
            except kombu_exceptions.TimeoutError:
                pass
            except(kombu_exceptions.ChannelLimitExceeded,
                   kombu_exceptions.ConnectionLimitExceeded,
                   kombu_exceptions.OperationalError,
                   kombu_exceptions.NotBoundError,
                   kombu_exceptions.MessageStateError,
                   kombu_exceptions.LimitExceeded) as err:
                raise ChannelFailureException("Error connecting to RabbitMQ, see inner exception for details", err)

        def _consume_errors(exc, interval: int)-> None:
            self._logger.error('Draining error: %s, will retry triggering in %s seconds', exc, interval, exc_info=True)

        def _read_message(body: str, msg: KombuMessage) -> None:
            self._logger.debug("Monitoring event received at: %s headers: %s payload: %s", datetime.utcnow().isoformat(), msg.headers, body)
            self._msg = msg
            self._message = self._message_factory.create_message(msg)

        connection = BrokerConnection(hostname=self._amqp_uri)
        with connections[connection].acquire(block=True) as conn:
            self._logger.debug('Got connection: %s', conn.as_uri())
            with Consumer(conn, queues=[self._queue], callbacks=[_read_message]) as consumer:
                consumer.qos(prefetch_count=1)
                ensure_kwargs = self.RETRY_OPTIONS.copy()
                ensure_kwargs['errback'] = _consume_errors
                safe_drain = conn.ensure(consumer, _consume, **ensure_kwargs)
                safe_drain(conn, timeout)

        return self._message
Beispiel #6
0
class ArameConsumer(BrightsideConsumer):
    """ Implements reading a message from an RMQ broker. It uses a queue, created by subscribing to a message topic

    """
    RETRY_OPTIONS = {
        'interval_start': 1,
        'interval_step': 1,
        'interval_max': 1,
        'max_retries': 3,
    }

    def __init__(self, connection: Connection, configuration: BrightsideConsumerConfiguration, logger: logging.Logger=None) -> None:
        self._exchange = Exchange(connection.exchange, type=connection.exchange_type, durable=connection.is_durable)
        self._routing_key = configuration.routing_key
        self._amqp_uri = connection.amqp_uri
        self._queue_name = configuration.queue_name
        self._routing_key = configuration.routing_key
        self._prefetch_count = configuration.prefetch_count
        self._is_durable = configuration.is_durable
        self._heartbeat = connection.heartbeat
        self._connect_timeout = connection.connect_timeout
        self._message_factory = ArameMessageFactory()
        self._logger = logger or logging.getLogger(__name__)
        self._conn = None
        consumer_arguments = {}
        if configuration.is_ha is True:
            consumer_arguments = {"x-ha-policy": "all"}

        self._is_long_running_handler = configuration.is_long_runing_handler

        self._queue = Queue(self._queue_name, exchange=self._exchange, routing_key=self._routing_key,
                            durable=self._is_durable, consumer_arguments=consumer_arguments)

        self._msg = None  # Kombu Message
        self._message = None  # Brightside Message

        self._establish_connection(BrokerConnection(hostname=self._amqp_uri, connect_timeout=self._connect_timeout, heartbeat=self._heartbeat))
        self._establish_channel()
        self._establish_consumer()

    def acknowledge(self, message: BrightsideMessage):
        if (self._message is not None) and self._message.id == message.id:
            self._msg.ack()
            self._msg = None

    def _ensure_connection(self):
        # We can get connection aborted before we try to read, so despite ensure()
        # we check the connection here
        if self._conn.connected is not True:
            self._conn = self._conn.clone()
            self._conn.ensure_connection(max_retries=3)
            self._channel = self._conn.channel()
            self._consumer.revive(self._channel)
            self._consumer.consume()

    def _establish_channel(self):
        self._channel = self._conn.channel()

    def _establish_consumer(self):
        self._consumer = Consumer(channel=self._channel, queues=[self._queue], callbacks=[self._read_message])
        self._consumer.qos(prefetch_count=1)
        self._consumer.consume()

    def _establish_connection(self, conn: BrokerConnection) -> None:
        """
        We don't use a pool here. We only have one consumer connection per process, so
        we get no value from a pool, and we want to use a heartbeat to keep the consumer
        collection alive, which does not work with a pool
        :return: the connection to the transport
        """
        try:
            self._logger.debug("Establishing connection.")
            self._conn = conn.ensure_connection(max_retries=3)
            self._logger.debug('Got connection: %s', conn.as_uri())
        except kombu_exceptions.OperationalError as oe:
            self._logger.error("Error connecting to RMQ, could not retry %s", oe)
            # Try to clean up the mess
            if self._conn is not None:
                self._conn.close()
            else:
                conn.close()

    def has_acknowledged(self, message):
        if (self._message is not None) and self._message.id == message.id:
            if self._msg is None:
                return True
            else:
                return False

    def purge(self, timeout: int = 5) -> None:

        def _purge_errors(exc, interval):
            self._logger.error('Purging error: %s, will retry triggering in %s seconds', exc, interval, exc_info=True)

        def _purge_messages(cnsmr: BrightsideConsumer):
            cnsmr.purge()
            self._message = None

        self._ensure_connection()

        ensure_kwargs = self.RETRY_OPTIONS.copy()
        ensure_kwargs['errback'] = _purge_errors
        safe_purge = self._conn.ensure(self._consumer, _purge_messages, **ensure_kwargs)
        safe_purge(self._consumer)

    def _read_message(self, body: str, msg: KombuMessage) -> None:
        self._logger.debug("Monitoring event received at: %s headers: %s payload: %s", datetime.utcnow().isoformat(), msg.headers, body)
        self._msg = msg
        self._message = self._message_factory.create_message(msg)

    def receive(self, timeout: int) -> BrightsideMessage:

        self._message = BrightsideMessage(BrightsideMessageHeader(uuid4(), "", BrightsideMessageType.MT_NONE), BrightsideMessageBody(""))

        def _consume(cnx: BrokerConnection, timesup: int) -> None:
            try:
                cnx.drain_events(timeout=timesup)
            except kombu_exceptions.TimeoutError:
                self._logger.debug("Time out reading from queue %s", self._queue_name)
                cnx.heartbeat_check()
            except(kombu_exceptions.ChannelLimitExceeded,
                   kombu_exceptions.ConnectionLimitExceeded,
                   kombu_exceptions.OperationalError,
                   kombu_exceptions.NotBoundError,
                   kombu_exceptions.MessageStateError,
                   kombu_exceptions.LimitExceeded) as err:
                raise ChannelFailureException("Error connecting to RabbitMQ, see inner exception for details", err)
            except (OSError, IOError, ConnectionError) as socket_err:
                self._reset_connection()
                raise ChannelFailureException("Error connecting to RabbitMQ, see inner exception for details", socket_err)

        def _consume_errors(exc, interval: int)-> None:
            self._logger.error('Draining error: %s, will retry triggering in %s seconds', exc, interval, exc_info=True)

        self._ensure_connection()

        ensure_kwargs = self.RETRY_OPTIONS.copy()
        ensure_kwargs['errback'] = _consume_errors
        safe_drain = self._conn.ensure(self._consumer, _consume, **ensure_kwargs)
        safe_drain(self._conn, timeout)

        return self._message

    def _reset_connection(self) -> None:
        self._logger.debug('Reset connection to RabbitMQ following socket error')
        self._conn.close()
        self._establish_connection(BrokerConnection(hostname=self._amqp_uri, connect_timeout=self._connect_timeout, heartbeat=self._heartbeat))
        self._establish_channel()
        self._establish_consumer()

    def requeue(self, message: BrightsideMessage) -> None:
        """
            TODO: has does a consumer resend
        """
        self._msg.requeue()

    def run_heartbeat_continuously(self) -> threading.Event:
        """
        For a long runing handler, there is a danger that we do not send a heartbeat message or activity on the
        connection whilst we are running the handler. With a default heartbeat of 30s, for example, there is a risk
        that a handler which takes more than 15s will fail to send the heartbeat in time and then the broker will
        reset the connection. So we spin up another thread, where the user has marked the thread as having a
        long-running thread
        :return: an event to cancel the thread
        """

        cancellation_event = threading.Event()

        # Effectively a no-op if we are not actually a long-running thread
        if not self._is_long_running_handler:
            return cancellation_event

        self._logger.debug("Running long running handler on %s", self._conn)

        def _send_heartbeat(cnx: BrokerConnection, period: int, logger: logging.Logger) -> None:
                while not cancellation_event.is_set():
                    cnx.heartbeat_check()
                    cancellation_event.wait(timeout= period)
                logger.debug("Signalled to exit long-running handler heartbeat")

        heartbeat_thread = threading.Thread(target=_send_heartbeat, args=(self._conn, 1, self._logger), daemon=True)
        self._logger.debug("Begin heartbeat thread for  %s", self._conn)
        heartbeat_thread.start()
        self._logger.debug("Heartbeat running on thread for  %s", self._conn)
        return cancellation_event

    def stop(self):
        if self._conn is not None:
            self._logger.debug("Closing connection: %s", self._conn)
            self._conn.close()
            self._conn = None
            
Beispiel #7
0
class ArameConsumer(BrightsideConsumer):
    """ Implements reading a message from an RMQ broker. It uses a queue, created by subscribing to a message topic

    """
    RETRY_OPTIONS = {
        'interval_start': 1,
        'interval_step': 1,
        'interval_max': 1,
        'max_retries': 3,
    }

    def __init__(self,
                 connection: Connection,
                 configuration: BrightsideConsumerConfiguration,
                 logger: logging.Logger = None) -> None:
        self._exchange = Exchange(connection.exchange,
                                  type=connection.exchange_type,
                                  durable=connection.is_durable)
        self._routing_key = configuration.routing_key
        self._amqp_uri = connection.amqp_uri
        self._queue_name = configuration.queue_name
        self._routing_key = configuration.routing_key
        self._prefetch_count = configuration.prefetch_count
        self._is_durable = configuration.is_durable
        self._message_factory = ArameMessageFactory()
        self._logger = logger or logging.getLogger(__name__)
        self._queue = Queue(self._queue_name,
                            exchange=self._exchange,
                            routing_key=self._routing_key,
                            durable=self._is_durable)
        self._msg = None  # Kombu Message
        self._message = None  # Brightside Message

        # TODO: Need to fix the argument types with default types issue

    def acknowledge(self, message: BrightsideMessage):
        if (self._message is not None) and self._message.id == message.id:
            self._msg.ack()
            self._msg = None

    def has_acknowledged(self, message):
        if (self._message is not None) and self._message.id == message.id:
            if self._msg is None:
                return True
            else:
                return False

    def purge(self, timeout: int = 5) -> None:
        def _purge_errors(exc, interval):
            self._logger.error(
                'Purging error: %s, will retry triggering in %s seconds',
                exc,
                interval,
                exc_info=True)

        def _purge_messages(cnsmr: BrightsideConsumer):
            cnsmr.purge()
            self._message = None

        connection = BrokerConnection(hostname=self._amqp_uri)
        with connections[connection].acquire(block=True) as conn:
            self._logger.debug('Got connection: %s', conn.as_uri())
            with Consumer(conn,
                          queues=[self._queue],
                          callbacks=[_purge_messages]) as consumer:
                ensure_kwargs = self.RETRY_OPTIONS.copy()
                ensure_kwargs['errback'] = _purge_errors
                safe_purge = conn.ensure(consumer, _purge_messages,
                                         **ensure_kwargs)
                safe_purge(consumer)

    def receive(self, timeout: int) -> BrightsideMessage:

        self._message = BrightsideMessage(
            BrightsideMessageHeader(uuid4(), "",
                                    BrightsideMessageType.MT_NONE),
            BrightsideMessageBody(""))

        def _consume(cnx: BrokerConnection, timesup: int) -> None:
            try:
                cnx.drain_events(timeout=timesup)
            except kombu_exceptions.TimeoutError:
                pass
            except (kombu_exceptions.ChannelLimitExceeded,
                    kombu_exceptions.ConnectionLimitExceeded,
                    kombu_exceptions.OperationalError,
                    kombu_exceptions.NotBoundError,
                    kombu_exceptions.MessageStateError,
                    kombu_exceptions.LimitExceeded) as err:
                raise ChannelFailureException(
                    "Error connecting to RabbitMQ, see inner exception for details",
                    err)

        def _consume_errors(exc, interval: int) -> None:
            self._logger.error(
                'Draining error: %s, will retry triggering in %s seconds',
                exc,
                interval,
                exc_info=True)

        def _read_message(body: str, msg: KombuMessage) -> None:
            self._logger.debug(
                "Monitoring event received at: %s headers: %s payload: %s",
                datetime.utcnow().isoformat(), msg.headers, body)
            self._msg = msg
            self._message = self._message_factory.create_message(msg)

        connection = BrokerConnection(hostname=self._amqp_uri)
        with connections[connection].acquire(block=True) as conn:
            self._logger.debug('Got connection: %s', conn.as_uri())
            with Consumer(conn,
                          queues=[self._queue],
                          callbacks=[_read_message]) as consumer:
                consumer.qos(prefetch_count=1)
                ensure_kwargs = self.RETRY_OPTIONS.copy()
                ensure_kwargs['errback'] = _consume_errors
                safe_drain = conn.ensure(consumer, _consume, **ensure_kwargs)
                safe_drain(conn, timeout)

        return self._message

    def requeue(self, message: BrightsideMessage) -> None:
        """
            TODO: has does a consumeure resend
        """
        self._msg.requeue()