Example #1
0
    async def connect(self):
        """Connect the service worker to th3e NATS/STAN Queue.

        Also handles reconnecting when the network has dropped the connection.
        Both the NATS and the STAN clients need to be reinstantiated to work correctly.

        """
        logger.info('Connecting...')
        if self.nc:
            try:
                logger.debug('close old NATS client')
                await self.nc.close()
            except asyncio.CancelledError as err:
                logger.debug('closing stale connection err:%s', err)
            finally:
                self.nc = None

        self.nc = NATS()
        self.sc = STAN()

        nats_connection_options = {
            **self.config.NATS_CONNECTION_OPTIONS,
            **{
                'loop': self._loop,
                'error_cb': error_cb
            },
            **self.nats_connection_options
        }

        stan_connection_options = {
            **self.config.STAN_CONNECTION_OPTIONS,
            **{
                'nats': self.nc,
                'conn_lost_cb': self._stan_conn_lost_cb,
                'loop': self._loop
            },
            **self.stan_connection_options
        }

        subscription_options = {
            **self.config.SUBSCRIPTION_OPTIONS,
            **{
                'cb': self.cb_handler
            },
            **self.subscription_options
        }

        await self.nc.connect(**nats_connection_options)
        await self.sc.connect(**stan_connection_options)
        await self.sc.subscribe(**subscription_options)

        logger.info(
            'Subscribe the callback: %s to the queue: %s.',
            subscription_options.get('cb').__name__
            if subscription_options.get('cb') else 'no_call_back',
            subscription_options.get('queue'))
Example #2
0
 async def conn_lost_cb(error):
     logger.info('Connection lost:%s', error)
     for i in range(0, 100):
         try:
             logger.info('Reconnecting, attempt=%i...', i)
             await self.connect()
         except Exception as e:  # pylint: disable=broad-except; catch all errors from client framework
             logger.error('Error %s',
                          e.with_traceback(),
                          stack_info=True)
             continue
         break
Example #3
0
async def cb_subscription_handler(msg: nats.aio.client.Msg):
    """Use Callback to process Queue Msg objects."""
    try:
        logger.info('Received raw message seq:%s, data=  %s', msg.sequence, msg.data.decode())
        payment_token = extract_payment_token(msg)
        logger.debug('Extracted payment token: %s', payment_token)
        process_filing(payment_token, FLASK_APP)
    except OperationalError as err:
        logger.error('Queue Blocked - Database Issue: %s', json.dumps(payment_token), exc_info=True)
        raise err  # We don't want to handle the error, as a DB down would drain the queue
    except (QueueException, Exception):  # pylint: disable=broad-except
        # Catch Exception so that any error is still caught and the message is removed from the queue
        capture_message('Queue Error:' + json.dumps(payment_token), level='error')
        logger.error('Queue Error: %s', json.dumps(payment_token), exc_info=True)
Example #4
0
    async def reconnected_cb():
        """Connect to the NATS services.

        This gets called when the client successfully connects, or reconnects.
        """
        logger.info('Connected to NATS at %s...', nc.connected_url.netloc)
Example #5
0
async def run(loop):  # pylint: disable=too-many-locals
    """Run the main application loop for the service.

    This runs the main top level service functions for working with the Queue.
    """
    # NATS client connections
    nc = NATS()
    sc = STAN()

    async def reconnected_cb():
        """Connect to the NATS services.

        This gets called when the client successfully connects, or reconnects.
        """
        logger.info('Connected to NATS at %s...', nc.connected_url.netloc)

    async def close():
        """Close the stream and nats connections."""
        await sc.close()
        await nc.close()

    # Connection and Queue configuration.
    def nats_connection_options():
        return {
            'servers':
            os.getenv('NATS_SERVERS', 'nats://127.0.0.1:4222').split(','),
            'io_loop':
            loop,
            'error_cb':
            error_cb,
            'closed_cb':
            closed_cb,
            'reconnected_cb':
            reconnected_cb,
            'name':
            os.getenv('NATS_CLIENT_NAME', 'entity.filing.worker')
        }

    def stan_connection_options():
        return {
            'cluster_id': os.getenv('NATS_CLUSTER_ID', 'test-cluster'),
            'client_id': str(random.SystemRandom().getrandbits(0x58)),
            'nats': nc
        }

    def subscription_options():
        return {
            'subject': os.getenv('NATS_SUBJECT', 'entity.filings'),
            'queue': os.getenv('NATS_QUEUE', 'filing-worker'),
            'durable_name':
            os.getenv('NATS_QUEUE', 'filing-worker') + '_durable',
            'cb': cb_subscription_handler
        }

    try:
        # Connect to the NATS server, and then use that for the streaming connection.
        await nc.connect(**nats_connection_options())
        await sc.connect(**stan_connection_options())

        # Attach the callback queue
        await sc.subscribe(**subscription_options())
        logger.info('Subscribe the callback: %s to the queue: %s.',
                    subscription_options().get('cb').__name__,
                    subscription_options().get('queue'))

        # register the signal handler
        for sig in ('SIGINT', 'SIGTERM'):
            loop.add_signal_handler(
                getattr(signal, sig),
                functools.partial(signal_handler,
                                  sig_loop=loop,
                                  sig_nc=nc,
                                  task=close))

    except Exception as e:  # pylint: disable=broad-except
        # TODO tighten this error and decide when to bail on the infinite reconnect
        logger.error(e)