Пример #1
0
    def __init__(self,
                 nsqd_tcp_addresses=None,
                 lookupd_http_addresses=None,
                 max_in_flight=42,
                 loop=None,
                 heartbeat_interval=30000,
                 feature_negotiation=True,
                 tls_v1=False,
                 snappy=False,
                 deflate=False,
                 deflate_level=6,
                 sample_rate=0,
                 consumer=False,
                 log_level=None,
                 auth_secret=None):
        self._config = {
            "deflate": deflate,
            "deflate_level": deflate_level,
            "sample_rate": sample_rate,
            "snappy": snappy,
            "tls_v1": tls_v1,
            "heartbeat_interval": heartbeat_interval,
            'feature_negotiation': feature_negotiation,
        }
        self._nsqd_tcp_addresses = nsqd_tcp_addresses or []
        self._lookupd_http_addresses = lookupd_http_addresses or []

        self._max_in_flight = max_in_flight
        self._loop = loop or asyncio.get_event_loop()
        self._queue = asyncio.Queue(loop=self._loop)

        self._connections = {}

        self._idle_timeout = 10

        self._rdy_control = None
        self._max_in_flight = max_in_flight

        self._is_subscribe = False
        self._redistribute_timeout = 5  # sec
        self._lookupd_poll_time = 30  # sec
        self.topic = None
        self._auth_secret = auth_secret.decode('utf-8') if isinstance(
            auth_secret, bytes) else auth_secret
        self._rdy_control = RdyControl(idle_timeout=self._idle_timeout,
                                       max_in_flight=self._max_in_flight,
                                       loop=self._loop)
Пример #2
0
class Reader:
    """
    NSQ tcp reader
    """
    def __init__(self,
                 nsqd_tcp_addresses=None,
                 lookupd_http_addresses=None,
                 max_in_flight=42,
                 loop=None,
                 heartbeat_interval=30000,
                 feature_negotiation=True,
                 tls_v1=False,
                 snappy=False,
                 deflate=False,
                 deflate_level=6,
                 sample_rate=0,
                 consumer=False,
                 log_level=None,
                 auth_secret=None):
        self._config = {
            "deflate": deflate,
            "deflate_level": deflate_level,
            "sample_rate": sample_rate,
            "snappy": snappy,
            "tls_v1": tls_v1,
            "heartbeat_interval": heartbeat_interval,
            'feature_negotiation': feature_negotiation,
        }
        self._nsqd_tcp_addresses = nsqd_tcp_addresses or []
        self._lookupd_http_addresses = lookupd_http_addresses or []

        self._max_in_flight = max_in_flight
        self._loop = loop or asyncio.get_event_loop()
        self._queue = asyncio.Queue()

        self._connections = {}

        self._idle_timeout = 10

        self._is_subscribe = False
        self._redistribute_timeout = 5  # sec
        self._lookupd_poll_time = 30  # sec
        self.topic = None
        self._auth_secret = auth_secret.decode('utf-8') if isinstance(
            auth_secret, bytes) else auth_secret
        self._rdy_control = RdyControl(idle_timeout=self._idle_timeout,
                                       max_in_flight=self._max_in_flight,
                                       loop=self._loop)

        self.clean_closed = False

    async def connect(self):
        logging.info('reader connecting')
        if self._lookupd_http_addresses:
            """
            because lookupd must find a topic to find nsqds
            so, lookupd connect changed in to subscribe func
            """
            pass
        if self._nsqd_tcp_addresses:
            for host, port in self._nsqd_tcp_addresses:
                conn = await create_connection(host,
                                               port,
                                               queue=self._queue,
                                               loop=self._loop)
                await self.prepare_conn(conn)
                self._connections[conn.id] = conn
            self._rdy_control.add_connections(self._connections)
        # init distribute for conns, init update rdy state for conn
        self._rdy_control.redistribute()

    async def prepare_conn(self, conn):
        conn._on_message = partial(self._on_message, conn)
        resp = await conn.identify(**self._config)
        resp = json.loads(_convert_to_str(resp))
        if resp.get('auth_required') is True:
            if not self._auth_secret:
                conn.close()
                self.close()
                raise ReaderError("Auth secret is required for NSQ connection")
            resp = await conn.auth(self._auth_secret)

    def _on_message(self, conn, msg):
        conn._last_message = time.time()
        if conn._on_rdy_changed_cb is not None:
            conn._on_rdy_changed_cb(conn.id)
        return msg

    async def _poll_lookupd(self, host, port):
        nsqlookup_conn = NsqLookupd(host, port, loop=self._loop)
        try:
            res = await nsqlookup_conn.lookup(self.topic)
            logger.info('lookupd response')
            logger.info(res)
        except Exception as tmp:
            logger.error(tmp)
            logger.exception(tmp)

        for producer in res['producers']:
            host = producer['broadcast_address']
            port = producer['tcp_port']
            tmp_id = "tcp://{}:{}".format(host, port)
            if tmp_id not in self._connections:
                logger.debug(('host, port', host, port))
                conn = await create_connection(host,
                                               port,
                                               queue=self._queue,
                                               loop=self._loop)
                logger.debug(('conn.id:', conn.id))
                self._connections[conn.id] = conn
                self._rdy_control.add_connection(conn)
        await nsqlookup_conn.close()

    async def subscribe(self, topic, channel):
        self.topic = topic
        self._is_subscribe = True
        if self._lookupd_http_addresses:
            await self._lookupd()
        for conn in self._connections.values():
            await self.sub(conn, topic, channel)
            if conn._on_rdy_changed_cb is not None:
                conn._on_rdy_changed_cb(conn.id)

        # redistribute is a task for fail or overload to
        # rebalance tcpconnections
        # consider for further. disable now
        # self._redistribute_task = self._loop.create_task(self._redistribute())

    async def sub(self, conn, topic, channel):
        await conn.execute(SUB, topic, channel)

    def wait_messages(self):
        if not self._is_subscribe:
            raise ValueError('You must subscribe to the topic first')

        while self._is_subscribe and (not self.clean_closed):
            fut = self._loop.create_task(self._queue.get())
            yield fut

        if self.clean_closed:
            while not self._queue.empty():
                fut = self._loop.create_task(self._queue.get())
                yield fut

    async def messages(self):
        if not self._is_subscribe:
            raise ValueError('You must subscribe to the topic first')

        while self._is_subscribe and (not self.clean_closed):
            result = await self._queue.get()
            yield result

    async def _redistribute(self):
        while self._is_subscribe:
            self._rdy_control.redistribute()
            await asyncio.sleep(self._redistribute_timeout, loop=self._loop)

    async def _lookupd(self):
        host, port = random.choice(self._lookupd_http_addresses)
        await self._poll_lookupd(host, port)

    async def requeue_msg_closed(self):
        logger.info("requeue_msg_closed")
        if self.clean_closed:
            while not self._queue.empty():
                result = await self._queue.get()
                await result.req()

    async def clean_close(self):
        logger.info("clean_close")
        self._rdy_control.close()
        self.clean_closed = True
        await self.requeue_msg_closed()
        for conn in self._connections.values():
            conn.close()

    def close(self, *args):
        logger.info("reader closed")
        self._rdy_control.close()
        self.clean_closed = True
        for conn in self._connections.values():
            conn.close()
Пример #3
0
class Reader:
    """
    NSQ tcp reader
    """
    def __init__(self,
                 nsqd_tcp_addresses=None,
                 lookupd_http_addresses=None,
                 max_in_flight=42,
                 loop=None,
                 heartbeat_interval=30000,
                 feature_negotiation=True,
                 tls_v1=False,
                 snappy=False,
                 deflate=False,
                 deflate_level=6,
                 sample_rate=0,
                 consumer=False,
                 log_level=None):
        self._config = {
            "deflate": deflate,
            "deflate_level": deflate_level,
            "sample_rate": sample_rate,
            "snappy": snappy,
            "tls_v1": tls_v1,
            "heartbeat_interval": heartbeat_interval,
            'feature_negotiation': feature_negotiation,
        }
        self._nsqd_tcp_addresses = nsqd_tcp_addresses or []
        self._lookupd_http_addresses = lookupd_http_addresses or []

        self._max_in_flight = max_in_flight
        self._loop = loop or asyncio.get_event_loop()
        self._queue = asyncio.Queue(loop=self._loop)

        self._connections = {}

        self._idle_timeout = 10

        self._rdy_control = None
        self._max_in_flight = max_in_flight

        self._is_subscribe = False
        self._redistribute_timeout = 5  # sec
        self._lookupd_poll_time = 30  # sec
        self.topic = None
        self._rdy_control = RdyControl(idle_timeout=self._idle_timeout,
                                       max_in_flight=self._max_in_flight,
                                       loop=self._loop)

    async def connect(self):
        logging.info('reader connecting')
        if self._lookupd_http_addresses:
            """
            because lookupd must find a topic to find nsqds
            so, lookupd connect changed in to subscribe func
            """
            pass
        if self._nsqd_tcp_addresses:
            for host, port in self._nsqd_tcp_addresses:
                conn = await create_connection(host,
                                               port,
                                               queue=self._queue,
                                               loop=self._loop)
                await self.prepare_conn(conn)
            self._connections[conn.id] = conn
            self._rdy_control.add_connections(self._connections)

    async def prepare_conn(self, conn):
        conn.rdy_state = 2
        conn._on_message = partial(self._on_message, conn)
        result = await conn.identify(**self._config)

    def _on_message(self, conn, msg):
        # should not be coroutine
        # update connections rdy state
        conn.rdy_state = int(conn.rdy_state) - 1

        conn._last_message = time.time()
        if conn._on_rdy_changed_cb is not None:
            conn._on_rdy_changed_cb(conn.id)
        return msg

    async def _poll_lookupd(self, host, port):
        nsqlookup_conn = NsqLookupd(host, port, loop=self._loop)
        try:
            res = await nsqlookup_conn.lookup(self.topic)
            logger.info('lookupd response')
            logger.info(res)
        except Exception as tmp:
            logger.error(tmp)
            logger.exception(tmp)

        for producer in res['producers']:
            host = producer['broadcast_address']
            port = producer['tcp_port']
            tmp_id = "tcp://{}:{}".format(host, port)
            if tmp_id not in self._connections:
                logger.debug(('host, port', host, port))
                conn = await create_connection(host,
                                               port,
                                               queue=self._queue,
                                               loop=self._loop)
                logger.debug(('conn.id:', conn.id))
                self._connections[conn.id] = conn
                self._rdy_control.add_connection(conn)
        await nsqlookup_conn.close()

    async def subscribe(self, topic, channel):
        self.topic = topic
        self._is_subscribe = True
        if self._lookupd_http_addresses:
            await self._lookupd()
        for conn in self._connections.values():
            await self.sub(conn, topic, channel)
            if conn._on_rdy_changed_cb is not None:
                conn._on_rdy_changed_cb(conn.id)
        self._redistribute_task = self._loop.create_task(self._redistribute())

    async def sub(self, conn, topic, channel):
        await conn.execute(SUB, topic, channel)

    def wait_messages(self):
        if not self._is_subscribe:
            raise ValueError('You must subscribe to the topic first')

        while self._is_subscribe:
            fut = self._loop.create_task(self._queue.get())
            yield fut

    async def messages(self):
        if not self._is_subscribe:
            raise ValueError('You must subscribe to the topic first')

        while self._is_subscribe:
            result = await self._queue.get()
            yield result

    def is_starved(self):
        conns = self._connections.values()
        return any(conn.is_starved() for conn in conns)

    async def _redistribute(self):
        while self._is_subscribe:
            self._rdy_control.redistribute()
            await asyncio.sleep(self._redistribute_timeout, loop=self._loop)

    async def _lookupd(self):
        host, port = random.choice(self._lookupd_http_addresses)
        await self._poll_lookupd(host, port)
Пример #4
0
class Reader:
    """
    NSQ tcp reader
    """

    def __init__(self, nsqd_tcp_addresses=None, lookupd_http_addresses=None,
                 max_in_flight=42, loop=None, heartbeat_interval=30000,
                 feature_negotiation=True,
                 tls_v1=False, snappy=False, deflate=False, deflate_level=6,
                 sample_rate=0, consumer=False, log_level=None):
        self._config = {
            "deflate": deflate,
            "deflate_level": deflate_level,
            "sample_rate": sample_rate,
            "snappy": snappy,
            "tls_v1": tls_v1,
            "heartbeat_interval": heartbeat_interval,
            'feature_negotiation': feature_negotiation,
        }
        self._nsqd_tcp_addresses = nsqd_tcp_addresses or []
        self._lookupd_http_addresses = lookupd_http_addresses or []

        self._max_in_flight = max_in_flight
        self._loop = loop or asyncio.get_event_loop()
        self._queue = asyncio.Queue(loop=self._loop)
        self._redistribute_task = None
        self._reconnect_task = None

        self._connections = {}

        self._idle_timeout = 10

        self._rdy_control = None
        self._max_in_flight = max_in_flight

        self._status = consts.INIT

        self._is_subscribe = False
        self._redistribute_timeout = 5  # sec
        self._lookupd_poll_time = 30  # sec
        self.topic = None
        self.channel = None
        self._rdy_control = RdyControl(idle_timeout=self._idle_timeout,
                                       max_in_flight=self._max_in_flight,
                                       loop=self._loop)

    async def connect(self):
        logging.info('reader connecting')
        if self._lookupd_http_addresses:
            """
            because lookupd must find a topic to find nsqds
            so, lookupd connect changed in to subscribe func
            """
            pass
        if self._nsqd_tcp_addresses:
            for host, port in self._nsqd_tcp_addresses:
                conn = await create_connection(
                    host, port, queue=self._queue,
                    loop=self._loop)
                await self.prepare_conn(conn)
            self._connections[conn.id] = conn
            self._rdy_control.add_connections(self._connections)
            self._status = consts.CONNECTED
        self._loop.create_task(self.auto_reconnect())

    async def prepare_conn(self, conn):
        conn.rdy_state = 2
        conn._on_message = partial(self._on_message, conn)
        result = await conn.identify(**self._config)

    def _on_message(self, conn, msg):
        # should not be coroutine
        # update connections rdy state
        conn.rdy_state = int(conn.rdy_state) - 1

        conn._last_message = time.time()
        if conn._on_rdy_changed_cb is not None:
            conn._on_rdy_changed_cb(conn.id)
        return msg

    async def _poll_lookupd(self, host, port):
        nsqlookup_conn = NsqLookupd(host, port, loop=self._loop)
        try:
            res = await nsqlookup_conn.lookup(self.topic)
            logger.info('lookupd response')
            logger.info(res)
        except Exception as tmp:
            logger.error(tmp)
            logger.exception(tmp)

        for producer in res['producers']:
            host = producer['broadcast_address']
            port = producer['tcp_port']
            tmp_id = "tcp://{}:{}".format(host, port)
            if tmp_id not in self._connections:
                logger.debug(('host, port', host, port))
                conn = await create_connection(
                    host, port, queue=self._queue,
                    loop=self._loop)
                logger.debug(('conn.id:', conn.id))
                self._connections[conn.id] = conn
                self._rdy_control.add_connection(conn)
        await nsqlookup_conn.close()

    async def subscribe(self, topic, channel):
        self.topic = topic
        self.channel = channel
        self._is_subscribe = True

        if self._lookupd_http_addresses:
            await self._lookupd()
        for conn in self._connections.values():
            await self.sub(conn, topic, channel)
            if conn._on_rdy_changed_cb is not None:
                conn._on_rdy_changed_cb(conn.id)
        if not self._redistribute_task:
            self._redistribute_task = self._loop.create_task(
                self._redistribute()
            )

    async def sub(self, conn, topic, channel):
        await conn.execute(SUB, topic, channel)

    def wait_messages(self):
        if not self._is_subscribe:
            raise ValueError('You must subscribe to the topic first')

        while self._is_subscribe:
            fut = self._loop.create_task(self._queue.get())
            yield fut

    async def messages(self):
        if not self._is_subscribe:
            raise ValueError('You must subscribe to the topic first')

        while self._is_subscribe:
            result = await self._queue.get()
            yield result

    async def reconnect(self, conn):
        logger.debug(f'reader reconnect {conn.id}')

        if not conn.closed:
            return

        conn = await create_connection(
            conn._host, conn._port, queue=self._queue, loop=self._loop)
        await self.prepare_conn(conn)

        logger.info(f'Connection {conn.id} established')
        self._connections[conn.id] = conn
        self._rdy_control.add_connections(self._connections)

        await self.subscribe(self.topic, self.channel)
        # TODO: _cmd_queue is not working here...
        await self._rdy_control._update_rdy(conn.id)

    async def auto_reconnect(self):
        logger.debug('reader autoreconnect')
        timeout_generator = retry_iterator(init_delay=0.1, max_delay=10.0)
        while True:
            logger.debug('autoreconnect check loop')

            if self._status != consts.RECONNECTING:
                for conn in list(self._connections.values()).copy():
                    if conn.closed:
                        logger.info('Reconnect reader {}'.format(conn.id))
                        self._status = consts.RECONNECTING

                        try:
                            await self.reconnect(conn)
                        except ConnectionError:
                            logger.error(f'Can not connect to: {conn.id}')
                self._status = consts.CONNECTED

            t = next(timeout_generator)
            await asyncio.sleep(t, loop=self._loop)

    def is_starved(self):
        conns = self._connections.values()
        return any(conn.is_starved() for conn in conns)

    async def _redistribute(self):
        while self._is_subscribe:
            self._rdy_control.redistribute()
            await asyncio.sleep(self._redistribute_timeout,
                                loop=self._loop)

    async def _lookupd(self):
        host, port = random.choice(self._lookupd_http_addresses)
        await self._poll_lookupd(host, port)

    def stop(self):
        self._is_subscribe = False
        if self._redistribute_task:
            self._redistribute_task.cancel()
        self._reconnect_task.cancel()
        for connection in self._connections:
            connection.close()
        self._loop.run_until_complete(self._rdy_control.stop())