class TestRedis(unittest.TestCase):
    def setUp(self):
        conf: dict = {
            "host": "127.0.0.1",
            "port": 6379,
            "db": 4,
        }
        self.client = RedisWrapper(conf)

    def testAddGroup(self):
        group_name = "test_group"
        self.client.addGroup(group_name)

    def testReaddGroup(self):
        group_name = "test_group"
        consumer = "chitu"
        result = self.client.readGroup(group_name, consumer)
        print("==========readgroup===========")
        print(result)
        print("===============================")

    def testPending(self):
        group_name = "test_group"
        result = self.client.xPending(group_name)
        print(result)

    def testReadPending(self):
        group_name = "test_group"
        consumer = "chitu"
        id = "1571038514316-0"
        result = self.client.readPending(group_name, consumer, id)
        print(result)

    def testACK(self):
        group_name = "test_group"
        id = "1571041740221-0"
        self.client.ack(group_name, id)
Exemple #2
0
class Transport:
    def __init__(self, conf, redis_address=None):
        self.to_where = conf['send_to_where']

        self.data_original = None
        self.name = None
        self.communication = Communication(conf)

        # Redis conf
        self.redis = RedisWrapper(redis_address)
        self.group = conf['data_stream']['group']
        self.consumer = conf['data_stream']['consumer']
        # create group for data_stream
        self.redis.addGroup(self.group)

        if self.to_where == 'influxdb':
            self.db = InfluxdbWrapper(conf['influxdb'])
        elif self.to_where == 'kafka':
            self.db = self.initKafka(conf['kafka'])
        elif self.to_where == 'mqtt':
            self.mqtt_conf = conf.get('mqtt', dict())
            self.mqtt_put_queue = Queue()
            self.mqtt = MqttWrapper(self.mqtt_conf)

    def initKafka(self, conf):
        while True:
            try:
                client = KafkaWrapper(conf)
                return client

            except Exception as err:
                log.exception(err)
                log.error("Can't init Kafka client, try again...")
                time.sleep(1)

    def work(self, *args):
        while True:
            # get and decompress data
            try:
                bin_data = self.getData()
                raw_data = self.unpack(bin_data)
                log.debug('Data from redis: {}'.format(raw_data))
            except exceptions.ResponseError as e:
                # NOGROUP for data_stream, recreate it.
                if "NOGROUP" in str(e):
                    log.error('{}, recreate group: {}'.format(
                        str(e), self.group))
                    self.redis.addGroup(self.group)
                raw_data = None
            except Exception as err:
                log.exception(err)
                raw_data = None

            # compress and send data
            if raw_data:
                data = self.pack(raw_data["data"])
                try:
                    # send data and ack data id
                    log.debug("Send data to {}.".format(self.to_where))
                    self.send(data)
                    log.debug("Redis ack data.")
                    self.redis.ack(self.group, raw_data["id"])
                except Exception as err:
                    log.exception(err)
                    time.sleep(3)

    def pending(self, *args):
        while True:
            try:
                pending_data = self.getPendingData()

            except exceptions.ResponseError as err:
                # NOGROUP for data_stream, recreate it.
                if "NOGROUP" in str(err):
                    log.exception(err)
                    self.redis.addGroup(self.group)
                pending_data = []
            except Exception as err:
                log.exception(err)
                pending_data = []

            if pending_data:
                for d in pending_data:
                    raw_data = self.unpack(d)
                    if raw_data:
                        data = self.pack(raw_data["data"])
                        try:
                            # send data and ack data id
                            log.debug("Send pending data to {}".format(
                                self.to_where))
                            self.send(data)
                            log.debug("Redis ack pending data.")
                            self.redis.ack(self.group, raw_data["id"])
                        except Exception as err:
                            log.exception(err)
                            log.debug(f'Err data is: {data}')
                            time.sleep(3)
            else:
                time.sleep(5)
                log.debug("No pending data.")

    def unpack(self, data):
        """
        Get data from redis and unpack it
        :return: dict
        """
        # data_len = self.redis.get_len("data_queue")
        if data:
            try:
                data["data"] = msgpack.unpackb(data["data"])
                raw_data = data["data"]
                for k in list(raw_data.keys()):
                    v = raw_data[k]
                    raw_data.pop(k)
                    raw_data[k.decode('utf-8')] = msgpack.unpackb(
                        v, encoding='utf-8')
                data["data"] = raw_data
            except Exception as err:
                traceback.print_exc()
                log.exception(err)
        else:
            log.info('Redis have no new data.')
            time.sleep(5)
        return data

    def pack(self, data):
        """
        Converts the data to the format required for the corresponding database
        :param data: dict
        :return: the format data, usually dict
        """
        if data:
            if self.to_where == 'influxdb':
                try:
                    measurement = data['table_name']
                    tags = data['fields'].pop('tags')

                    unit = data['fields'].pop('unit')

                    fields = data['fields']
                    timestamp = data['time']

                    json_data = [{
                        'measurement': measurement,
                        'tags': tags,
                        'time': timestamp,
                        'fields': fields,
                        'unit': unit
                    }]
                    return json_data
                except Exception as err:
                    log.exception(err)
            elif self.to_where == 'kafka':
                try:
                    json_data = self.db.pack(data)
                    return json_data
                except Exception as err:
                    traceback.print_exc()
                    log.exception(err)
            elif self.to_where == 'mqtt':
                try:
                    schema, table = data.get('table_name').split('.')
                    data['fields'].pop('tags', None)
                    data['fields'].pop('unit', None)
                    data.pop('table_name', None)

                    fields = data['fields']
                    ts = data['time']
                    timestamp = datetime.fromtimestamp(ts).strftime(
                        "%Y-%m-%d %H:%M:%S")

                    json_data = {
                        "timestamp": timestamp,
                        "schema": schema,
                        "table": table,
                        "deviceid": table,
                        "fields": fields
                    }
                    return json_data
                except Exception as err:
                    raise err
            else:
                data['fields'].pop('tags')
                data['fields'].pop('unit')

    def send(self, data):
        """
        Sent to the corresponding database
        :param data: dict
        :return: None
        """
        if self.to_where == 'influxdb':
            time_precision = data[0].pop('unit')
            try:
                info = self.db.send(data, time_precision)
            except InfluxDBClientError as e:
                timestamp = data[0]['time'] / 1000000
                t_string = datetime.utcfromtimestamp(timestamp).strftime(
                    '%Y-%m-%d %H:%M:%S')
                # 2021-07-19 zhy: 为防止超过保留策略的数据导致 chitu 传数据卡死.
                if 'points beyond retention policy dropped' in str(e):
                    log.warning(
                        'Data beyond influx retention policy, timestamp is: {}, means {}'
                        .format(timestamp, t_string))
                    log.warning('Data is: {}'.format(data))
                    log.warning('Drop it.')
                    info = 'Drop data because data beyond influx retention policy.'
                # https://10.7.0.117:9091/mabo_group/base_application/doctopus/issues/3
                # 2021-07-19 zhy: 为防止数据因未知原因导致parse错误, drop掉. 防止卡死.
                elif 'invalid field format' in str(e):
                    log.warning(
                        'Data parse error, influx can`t receive it, timestamp is: {}, means {}'
                        .format(timestamp, t_string))
                    log.warning('Data is: {}'.format(data))
                    log.warning('Drop it.')
                    info = 'Drop data because parse error, influx can`t receive it.'
                else:
                    raise e

            self.communication.data[data[0]["measurement"]] = data
            if info:
                log.info('Send data to inflxudb.{}, {}'.format(
                    data[0]['measurement'], info))
            else:
                raise Exception("\nCan't connect influxdb")

        elif self.to_where == 'kafka':
            try:
                log.debug("Stuck here in send kafka data.")
                self.db.sendMessage(data)
                log.info('Send data to kafka: {}'.format(
                    data["dims"]["data_name"]))
            except Exception as e:
                raise e
        elif self.to_where == 'mqtt':
            self.mqtt_put_queue.put(data)
            try:
                self.mqtt.pubMessage(self.mqtt_put_queue)
                log.info('Publish data to MQTT topics({}): {}'.format(
                    self.mqtt_conf.get('topics', list()), data))
            except Exception as err:
                log.exception(err)

    def getData(self):
        """Get data from data_stream
        return:
            data: dict; {id:string, data:bytes}
        """
        data = self.redis.readGroup(self.group, self.consumer)
        if not data:
            return None
        else:
            id, raw = data[0][1][0]
            return {
                "id": id.decode(),
                "data": raw[b'data'],
            }

    def getPendingData(self):
        """Get pending data from data_stream

        return:
            data: [dict{"id":string, "data":bytes}]
                  dict{id:string, data:bytes}
        """
        data = self.redis.readPending(self.group, self.consumer, 0)
        res = []
        if not data:
            return res
        else:
            for v in data[0][1]:
                id, raw = v
                res.append({"id": id.decode(), "data": raw[b'data']})
            return res

    def reque_data(self):
        """
        return data to redis
        :return:
        """
        self.redis.queue_back('data_queue', self.data_original)

    def re_load(self):
        conf = get_conf()
        self.to_where = conf['send_to_where']
        self.data_original = None
        self.name = None

        if self.to_where == 'influxdb':
            self.db = InfluxdbWrapper(conf['influxdb'])
        elif self.to_where == 'kafka':
            self.db = KafkaWrapper(conf['kafka'])
        elif self.to_where == 'mqtt':
            self.mqtt_conf = conf.get('mqtt', dict())
            self.mqtt = MqttWrapper(self.mqtt_conf)
            self.mqtt_put_queue = Queue()
        return self