Springboot は TDengine を統合してデータ サブスクリプションを実現 - マルチスレッドの高速消費

1. TDengine データ購読サービス

アプリケーションがリアルタイムで TDengine に書き込まれたデータを取得したり、イベントの到着順にデータを処理したりできるようにするために、TDengine はメッセージ キュー製品と同様のデータ サブスクリプションおよび消費インターフェイスを提供します。このように、多くのシナリオで、TDengine を使用する時系列データ処理システムは、Kafka などのメッセージ キュー製品を統合する必要がなくなり、システム設計の複雑さを簡素化し、運用と保守のコストを削減します。

TDengine データ サブスクリプション サービスの詳細なドキュメントについては、公式 Web サイトを参照してください: TDengine—データ サブスクリプション

ここではこれ以上説明しません, この記事の主な内容: モノのインターネットのコンテキストでは, リアルタイムのデータ監視は非常に一般的なビジネス機能です. PBレベルのデータに直面してリアルタイムのデータを効率的に取得するにはどうすればよいですか?ボリューム? TDengine は優れたタイミング データベースは、Kafka メッセージ キューなどのサービスを統合します。メッセージキューは非同期デカップリングとピーク解消の役割を果たすことができますが、一般的に言えば、データの送信速度はデータ消費の速度よりもはるかに高速であるため (業務用の消費ロジックがあるため)、データが蓄積される可能性は非常に高くなります。ですから、消費スピードを上げることが当然最優先事項です。

2.マルチスレッドのバッチ消費

2.0の準備

データベースの作成: tmqdb

スーパー テーブルを作成します。

CREATE TABLE meters( tsTIMESTAMP、currentFLOAT、voltageINT)
タグ ( groupidINT、locationBINARY(16))

创建子表d0和d1:INSERT INTO d1USING metersTAGS(1, 'サンフランシスコ') 値(現在 - 9s, 10.1, 119)
INSERT INTOd0値(現在 - 8s, NULL, NULL)

トピックの作成: メーターから select * としてトピック topic_name を作成します

頼る:

    <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--  TDengine Java Connector       -->
        <dependency>
            <groupId>com.taosdata.jdbc</groupId>
            <artifactId>taos-jdbcdriver</artifactId>
            <version>3.0.0</version>
     </dependency>

2.1 スーパーテーブルエンティティクラス

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Meters {
    
    

    //动态属性
    private Timestamp ts;
    private float current;
    private int voltage;
    //静态属性
    private int groupid;
    private String location;
}

2.2 シミュレートされたデータ挿入


/**
 * 模拟写数据
 *
 * @author zhmsky
 * @date 2022/9/12 17:18
 */
public class WriteData {
    
    

    private static int count = 0;

    public static void main(String[] args) {
    
    
        TaosCrudUtils taosCrudUtils = new TaosCrudUtils();
        //一次性插入两万数据
        while (count < 20000) {
    
    
            Random random = new Random();
            int i = random.nextInt(235);
            String sql = "INSERT INTO tmqdb.d1 VALUES(now, " + (float) i + ", " + i + ");";
            taosCrudUtils.insert(sql);
            count++;
        }
    }

}

2.3 設定ファイル

#  服务器主机
taos.hostName=localdomain.com:6030
# 消费组
taos.groupId=test
# 主题名
taos.topicName=topic_name

2.4 コンシ​​ューマ マルチスレッド バッチ消費

package com.zhmsky.springboottdengine.数据订阅.消费者多线程消费;

import com.taosdata.jdbc.tmq.ConsumerRecords;
import com.taosdata.jdbc.tmq.TMQConstants;
import com.taosdata.jdbc.tmq.TaosConsumer;
import com.zhmsky.springboottdengine.数据订阅.pojo.Meters;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.time.Duration;
import java.util.Collections;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author zhmsky
 * @date 2022/9/12 19:58
 */
@Component
public class ConsumerHandler {
    
    

    private static String HOST_NAME;
    private static String GROUP_ID;
    private static String TOPICNAME;

    private TaosConsumer<Meters> consumer;
    private ExecutorService executors;

    //消息队列消息拉取是否开启
    private boolean active = true;

    public static Properties initConfig() {
    
    
        //获取配置文件
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("application.properties");
        Properties fileProperties = new Properties();
        try {
    
    
            //读取配置文件
            fileProperties.load(is);
            HOST_NAME = fileProperties.getProperty("taos.hostName");
            GROUP_ID = fileProperties.getProperty("taos.groupId");
            TOPICNAME = fileProperties.getProperty("taos.topicName");
        } catch (IOException e) {
    
    
            throw new RuntimeException(e);
        }

        //消费者配置
        Properties properties = new Properties();
        //连接地址
        properties.setProperty(TMQConstants.BOOTSTRAP_SERVERS, HOST_NAME);
        //允许从消息中解析表名
        properties.setProperty(TMQConstants.MSG_WITH_TABLE_NAME, "true");
        //开启自动提交
        properties.setProperty(TMQConstants.ENABLE_AUTO_COMMIT, "true");
        properties.setProperty(TMQConstants.GROUP_ID, GROUP_ID);
        //值解析方法,需要实现com.taosdata.jdbc.tmq.Deserializer 接口或继承 com.taosdata.jdbc.tmq.ReferenceDeserializer 类
        properties.setProperty(TMQConstants.VALUE_DESERIALIZER,
                "com.zhmsky.springboottdengine.数据订阅.MetersDeserializer");
        return properties;
    }

    /**
     * 项目启动时完成初始化配置
     */
    @PostConstruct
    public void initTaosConfig() {
    
    
        Properties properties = initConfig();
        try {
    
    
            //创建消费者实例
            consumer = new TaosConsumer<>(properties);
            //订阅主题
            consumer.subscribe(Collections.singletonList(TOPICNAME));
        } catch (SQLException e) {
    
    
            throw new RuntimeException(e);
        }

    }

    /**
     * 多线程批量消费(执行这个方法即可循环拉取消息)
     *
     * @param workerNum
     */
    public void execute(int workerNum) {
    
    
        executors = new ThreadPoolExecutor(workerNum, 20, 10,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(2000), new ThreadPoolExecutor.CallerRunsPolicy());
        while (active) {
    
    
            try {
    
    
                ConsumerRecords<Meters> records = consumer.poll(Duration.ofMillis(100));
                if (!records.isEmpty()) {
    
    
                    //将消息交给线程池认领
                    executors.submit(new TaskWorker(records));
                }
            } catch (SQLException e) {
    
    
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 停止拉取消息
     */
    public void stopTaosPoll() {
    
    
        this.active = false;
    }
}

2.5 カスタム スレッド クラス (実消費ロジックの処理)

/**
 * @author zhmsky
 * @date 2022/9/12 20:29
 */
@Slf4j
public class TaskWorker implements Runnable {
    
    

    private ConsumerRecords<Meters> consumerRecords;

    public TaskWorker(ConsumerRecords<Meters> records) {
    
    
        this.consumerRecords = records;
    }

    /**
     * 线程处理逻辑(正真的消息消费逻辑)
     */
    @Override
    public void run() {
    
    
        //TODO 真实的消费处理逻辑
        for (Meters consumerRecord : consumerRecords) {
    
    
            log.info(Thread.currentThread().getName() + "::" + consumerRecord.getTs() + " " + consumerRecord.getCurrent() + " "
                    + consumerRecord.getVoltage() + " " + consumerRecord.getLocation() + " " + consumerRecord.getGroupid());
        }
    }
}

2.6 値の解析

/**
 * 值解析方法
 *
 * @author zhmsky
 * @date 2022/9/12 16:43
 */
public class MetersDeserializer extends ReferenceDeserializer<Meters> {
    
    

}

2.7 テスト

@SpringBootApplication
@EnableScheduling
public class SpringbootTDengineApplication {
    
    
    @Autowired
    private ConsumerHandler consumers;

    public static void main(String[] args) {
    
    
        SpringApplication.run(SpringbootTDengineApplication.class, args);
    }

    //定时任务启动
    @Scheduled(cron = "* 3 21 * * ? ")
    public void test() {
    
    
        consumers.execute(10);
    }

}

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_42194695/article/details/126823147