def run():
    # Use borrowed connection for NATS then mount NATS Streaming
    # client on top.
    nc = NATS()
    nc.connect()

    # Start session with NATS Streaming cluster.
    sc = STAN()
    sc.connect("test-cluster", "client-123", nats=nc)
    
    total_messages = 0
    #future = asyncio.Future(loop=loop)
    def queue_cb(msg):
        #nonlocal future
        nonlocal total_messages
        print("Received a message (seq={}): {}".format(msg.seq, msg.data.decode()))
        total_messages += 1
        print(total_messages)
        r = binary_to_dict(msg.data.decode())
        print(r)
    #    if total_messages >= 2:
    #        future.set_result(None)

    # Subscribe to get all messages since beginning.
    sub = sc.subscribe("hi", queue="bar", cb=queue_cb)
Esempio n. 2
0
File: natsio.py Progetto: jplf/jplab
class NatsIo(object):
    """
    asyncio NATS client

    """
    def __init__(self,
                 host='127.0.0.1',
                 port='4222',
                 streaming_id=None,
                 cluster='svom-cluster'):
        self.nc = NATS()
        self.server = host
        self.is_connected = False
        self.streaming_id = streaming_id
        self.cluster = cluster
        self.sc = None
        self.loop = None
        try:
            self.loop = asyncio.get_event_loop()
        except RuntimeError:
            LOG.info(
                'Failed to retrieve asyncio event loop. Creating a new one...')
            self.loop = asyncio.new_event_loop()

        self.loop.run_until_complete(self._connect(servers=[self.server]))
        if streaming_id is not None:
            if has_streaming is True:
                self.sc = STAN()
                self.loop.run_until_complete(self._stan_connect())
            else:
                LOG.error("Can't run client without asyncio-nats-streaming")
                sys.exit(1)

        self.nats_server = NATSClient(self.nc, self.loop)
        self.nats_server.start()

    def is_streaming(self):
        return True if self.streaming_id is not None else False

    def _connect(self, servers):
        """connect"""
        while not self.is_connected:
            try:
                LOG.info('Attempting connection to nats server ' + servers[0])
                yield from self.nc.connect(servers=servers, io_loop=self.loop)
                yield from self.nc.flush()
            except Exception:
                LOG.exception('Connection failed.')
                LOG.info('Retrying connection in 5s.')
                yield from asyncio.sleep(5)
            else:
                self.is_connected = True
                LOG.info('Connected.')

        if self.streaming_id is not None:
            self.is_connected = False

    @asyncio.coroutine
    def _stan_connect(self):
        """connect"""
        while not self.is_connected:
            try:
                LOG.info('Attempting connection to server cluster \'' +
                         self.cluster + '\' with clientID \'' +
                         self.streaming_id + '\'')
                yield from self.sc.connect(self.cluster,
                                           self.streaming_id,
                                           nats=self.nc)
            except Exception:
                LOG.exception('Connection failed.')
                LOG.info('Retrying streaming server connection in 5s.')
                yield from asyncio.sleep(5)
            else:
                self.is_connected = True
                LOG.info('Connected to streaming server.')

    def subscribe(self, subject, handler=None):
        """launch subscription as async task"""
        if not self.is_connected:
            LOG.info("Awaiting to be connected to subscribe")
            self.loop.call_later(5, self.subscribe, subject, handler)
        if handler is None:
            handler = self._default_handler
        if self.streaming_id is None:
            asyncio.run_coroutine_threadsafe(self._subscribe(subject, handler),
                                             loop=self.loop)
        else:
            asyncio.run_coroutine_threadsafe(self._stan_subscribe(
                subject, handler),
                                             loop=self.loop)

    @asyncio.coroutine
    def _subscribe(self, subject, handler):
        """new subscription or handler change"""
        LOG.info('Subscribing to subject \'' + subject + '\'')
        try:
            yield from self.nc.subscribe(subject, cb=handler)
        except Exception:
            LOG.exception('Subscription failed.')
        else:
            LOG.info('Subscribed to subject  \'' + subject + '\'')

    @asyncio.coroutine
    def _stan_subscribe(self, subject, handler):
        """new subscription or handler change"""
        LOG.info('Subscribing to subject \'' + subject + '\'')
        try:
            # No need to custom durable name since 'clientID+durable name' is checked
            yield from self.sc.subscribe(subject,
                                         durable_name=handler.__name__,
                                         cb=handler)
        except Exception as e:
            LOG.exception('Subscription to \'%s\' failed' % subject)
        else:
            LOG.info('Handler \'%s\' subscribed to subject \'%s\'' %
                     (handler.__name__, subject))

    def publish(self, subject, data, with_reply=False):
        """launch publish as async task"""
        if isinstance(data, str):
            data = data.encode()
        if with_reply is False:
            if self.streaming_id is None:
                asyncio.run_coroutine_threadsafe(self._publish(subject, data),
                                                 loop=self.loop)
            else:
                asyncio.run_coroutine_threadsafe(self._stan_publish(subject,\
                                                 data), loop=self.loop)
        else:
            if self.streaming_id is None:
                future = asyncio.run_coroutine_threadsafe(\
                    self._publish(subject,\
                    data, with_reply=True), loop=self.loop)
                try:
                    result = future.result(1)
                    if result is None:
                        LOG.error('Message received no response')
                        return None
                except Exception:
                    LOG.exception('Timed request publishing exception')
                else:
                    return result
            else:
                LOG.error('NATS Streaming publish cannot handle replies')

    @asyncio.coroutine
    def _publish(self, subject, data, with_reply=False):
        """Nats transport data : bytes or str"""
        try:
            if with_reply is False:
                yield from self.nc.publish(subject, data)
            else:
                yield from self.nc.timed_request(subject, data)
        except Exception as e:
            LOG.exception('Publication failed.')

    @asyncio.coroutine
    def _stan_publish(self, subject, data):
        """Nats transport data : bytes or str"""
        try:
            yield from self.sc.publish(subject,\
                                     data, ack_handler=self._ack_handler)
        except Exception as e:
            LOG.exception('Publication failed, is \'' + \
                           subject + '\' a valid channel?')

    @asyncio.coroutine
    def _default_handler(self, msg):
        """Application handler data : str"""
        LOG.debug("--- Received: " + msg.subject + msg.data.decode())
        if self.streaming_id is not None:
            yield from self.sc.ack(msg)

    @asyncio.coroutine
    def ack(self, msg):
        yield from self.sc.ack(msg)

    @asyncio.coroutine
    def _ack_handler(self, ack):
        LOG.debug('Delivery ack received: {}'.format(ack.guid))

    def stop(self):
        LOG.debug('Trying to stop properly NatsIo: ')
        asyncio.run_coroutine_threadsafe(self.nc.flush(), loop=self.loop)
        asyncio.run_coroutine_threadsafe(self.nc.close(), loop=self.loop)
        try:
            time.sleep(1)
            self.loop.call_soon_threadsafe(self.loop.stop)
        except Exception as e:
            LOG.debug(e)
        try:
            time.sleep(1)
            if self.loop.is_running():
                LOG.debug('loop.is_running: ' + str(self.loop.is_running()))
                self.loop.call_soon_threadsafe(self.loop.close())
        except Exception as e:
            LOG.debug(e)
        self.nats_server.join()