Flink系列-9、Flink DataStream的输入数据集Data Source

news2024/10/5 20:23:41

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

大数据系列文章目录

官方网址:https://flink.apache.org/

学习资料:https://flink-learning.org.cn/
在这里插入图片描述

目录

  • addSource方法
  • 接口: SourceFunction
  • Flink自带的创建Source的方法
  • 自定义实现Source的注意事项
  • 继承关系
  • Source定义演示
    • 基于本地集合的非并行Source
    • 基于本地集合的并行Source
    • 基于文件的source
    • 基于网络套接字的source
    • 自定义的source(Custom-source)
      • 自定义实现SourceFunction接口
    • 实现ParallelSourceFunction创建可并行Source
    • 实现RichParallelSourceFunction:创建并行并带有Rich功能的Source
    • 基于kafka的source操作
    • 基于mysql的source操作

addSource方法

Flink提供了一个方法 通过env对象调用addSource方法,需要传入一个SourceFunction接口的实例化对象
这个方法是最根本的定义Source的方法。

我们如果想要定义Source,可以调用这个方法来得到DataStream对象

接口: SourceFunction

调用addSource方法需要传入接口SourceFunction的实例化对象(接口实现的子类)
在Flink中,所有创建Source的方式,在继承关系上,顶级父类均是这个SourceFunction接口

在这里插入图片描述
在这里插入图片描述

我们如果调用addSource方法来获取Source对象可以自定义SourceFunction的实现来完成方法调用。

Flink自带了SourceFunction的许多已实现的子类,最常用的有3个:

  • RichSourceFunction(抽象类) 带有Rich功能的SourceFunction接口实现类(不可并行)
  • ParallelSourceFunction(接口) 可并行的SourceFunction接口实现类
  • RichParallelSourceFunction(抽象类) 可并行的带有Rich功能SourceFunction接口实现类

那么,我们在调用addSource方法的时候,除了可以实现SourceFunction接口外,这3个常见的实现我们也可以继承使用。

Flink自带的创建Source的方法

除了使用addSource方法创建Source外,Flink也自带了许多方法供我们快捷创建对应的Source

  • fromElements 从元素中获取数据, 不可并行, 底层调用的fromCollection
  • fromCollection 从集合中获取数据,不可并行,底层调用的addSource方法,传入的是FromElementsFunction类(SourceFunction接口的实现子类)
  • socketTextStream 从socket中获取数据,不可并行,底层调用的addSource方法,传入的SocketTextStreamFunction类(SourceFunction的实现子类)
  • generateSequence 生成一个序列,可以并行,底层调用的StatefulSequenceSource(RichParallelSourceFunction的实现子类)
  • fromParallelCollection 从集合中获取数据,可以并行,底层addSource方法,传入的FromSplittableIteratorFunction(RichParallelSourceFunction的实现)
  • readTextFile 从文件中获取数据,可以并行,底层addSource方法,传入的ContinuousFileMonitoringFunction(RichSourceFunction的实现)

我们可以根据需求,调用上面的方法来获取数据
也可以调用addSource方法,自行提供自定义的实现

自定义实现Source的注意事项

如果想要使用addSource方法自定义实现Source,关于并行度需要注意:

  • 直接提供SourceFunction的实现,不可并行
  • 直接提供RichSourceFunction的实现,不可并行
  • 直接提供ParallelSourceFunction的实现,可以并行
  • 直接提供RichParallelSourceFunction,可以并行

我们自行根据是否需要并行来选择对应的接口或抽象类来实现。

继承关系

在这里插入图片描述

Source定义演示

基于本地集合的非并行Source

package batch.source;

import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

import java.util.Arrays;

/**
 * @author lwh
 * @date 2023/4/20
 * @description 基于本地集合的非并行source
 **/
public class BasicSourceDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<String> source1 = env.fromElements("hadoop", "spark", "flink");
        DataStreamSource<String> source2 = env.fromCollection(Arrays.asList("hadoop", "spark", "flume"));

//        source1.print().setParallelism(1);
        source2.print();

        System.out.println(env.getExecutionPlan());
        env.execute();

    }

}

在这里插入图片描述

基于本地集合的并行Source

package batch.source;

import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.NumberSequenceIterator;

/**
 * @author lwh
 * @date 2023/4/20
 * @description 基于本地集合的并行source
 **/
public class BasicParallelSourceDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<Long> source1 = env.generateSequence(1, 10).setParallelism(6);

        // fromParallelCollection 只接受SplittableIterator的子类
        // 也就是只能是:NumberSequenceIterator或者LongValueSequenceIterator
        DataStreamSource<Long> source2 = env.fromParallelCollection(
                new NumberSequenceIterator(1, 10), TypeInformation.of(Long.TYPE));

        source1.print();
        source2.setParallelism(3).print();

        System.out.println(env.getExecutionPlan());
        env.execute();

    }
}

在这里插入图片描述

基于文件的source

Flink的流处理可以直接通过readTextFile()方法读取文件来创建数据源,方法如下:

package batch.source;

import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * @author lwh
 * @date 2023/4/20
 * @description 基于文件的source
 **/
public class FileSourceDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<String> localFileSource = env.readTextFile("data/input/score.csv", "UTF-8");

        // DataStreamSource<String> hdfsFileSource = env.readTextFile("hdfs://node1:8020/input/license.txt");

        localFileSource.print();
        // hdfsFileSource.print();

        env.execute();

    }
}

在这里插入图片描述

基于网络套接字的source

上面两种方式创建的数据源一般都是固定的.如果需要源源不断的产生数据,可以使用socket的方式来获取数据,通过调用socketTextStream()方法

示例
编写Flink程序,接收socket的单词数据,并以空格进行单词拆分打印。

步骤

  • 获取流处理运行环境
  • 构建socket流数据源,并指定IP地址和端口号
  • 对接收到的数据进行空格拆分
  • 打印输出
  • 启动执行
  • 在Linux中,使用nc -lk 端口号监听端口,并发送单词
package batch.source;

import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * @author lwh
 * @date 2023/4/20
 * @description 基于网络套接字的source
 **/
public class SocketSourceDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        // 参数1 要连接的主机名 参数2 要连接的主机端口
        DataStreamSource<String> socketSource = env.socketTextStream("node1", 9999);

        socketSource.print();
        env.execute();

    }
}

自定义的source(Custom-source)

自定义实现SourceFunction接口

除了预定义的Source外,我们还可以通过实现SourceFunction来自定义Source,然后通过StreamExecutionEnvironment.addSource(sourceFunction)添加进来。

示例
自定义数据源, 每1秒钟随机生成一条订单信息(订单ID、用户ID、订单金额、时间戳)

要求

  • 随机生成订单ID(UUID)
  • 随机生成用户ID(0-2)
  • 随机生成订单金额(0-100)
  • 时间戳为当前系统时间

开发步骤

  • 创建订单实体类
  • 创建自定义数据源
  • 死循环生成订单
  • 随机构建订单信息
  • 上下文收集数据
  • 每隔一秒执行一次循环
  • 获取流处理环境
  • 使用自定义Source
  • 打印数据
  • 执行任务

订单实体类

package entity;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * @author lwh
 * @date 2023/4/20
 * @description
 **/
@Data
@AllArgsConstructor
public class Order {
    private String id;
    private String userId;
    private int money;
    private Long time;

}

流处理代码

package batch.source;

import entity.Order;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.SourceFunction;

import java.util.Random;
import java.util.UUID;

/**
 * @author lwh
 * @date 2023/4/20
 * @description 自定义实现SourceFunction接口
 **/
public class CustomerSourceWithoutParallelDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<Order> mySource = env.addSource(new MySource());

        mySource.print();
        env.execute();

    }

    public static class MySource implements SourceFunction<Order> {
        private boolean isRun = true;   // 关闭循环标记
        @Override
        public void run(SourceContext<Order> ctx) throws Exception {
            Random random = new Random();
            while (isRun) {
                String id = UUID.randomUUID().toString();
                String userId = random.nextInt(99) + "";
                int money = random.nextInt(999);
                long time = System.currentTimeMillis();

                ctx.collect(new Order(id, userId, money, time));
                Thread.sleep(1000L);
            }
        }

        @Override
        public void cancel() {
            this.isRun = false;
        }
    }

}

在这里插入图片描述

实现ParallelSourceFunction创建可并行Source

package batch.source;

import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.ParallelSourceFunction;

import java.util.UUID;

/**
 * @author lwh
 * @date 2023/4/20
 * @description 自定义多并行度Source
 **/
public class CustomerSourceWithParallelDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<String> mySource = env.addSource(new MySource()).setParallelism(6);
        mySource.print();

        env.execute();

    }

    public static class MySource implements ParallelSourceFunction<String> {
        @Override
        public void run(SourceContext<String> ctx) throws Exception {
            ctx.collect(UUID.randomUUID().toString());
            /*
            如果不设置无限循环可以看出,设置了多少并行度就打印出多少条数据
             */
        }

        @Override
        public void cancel() {}
    }

}

在这里插入图片描述

实现RichParallelSourceFunction:创建并行并带有Rich功能的Source

package batch.source;

import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.RichParallelSourceFunction;

import java.util.UUID;

/**
 * @author lwh
 * @date 2023/4/20
 * @description 自定义一个RichParallelSourceFunction的实现
 **/
public class CustomerRichSourceWithParallelDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        DataStreamSource<String> mySource = env.addSource(new MySource()).setParallelism(6);
        mySource.print();

        env.execute();

    }

    /*
   Rich 类型的Source可以比非Rich的多出有:
   - open方法,实例化的时候会执行一次,多个并行度会执行多次的哦(因为是多个实例了)
   - close方法,销毁实例的时候会执行一次,多个并行度会执行多次的哦
   - getRuntime方法可以获得当前的Runtime对象(底层API)
    */
    public static class MySource extends RichParallelSourceFunction<String> {
        @Override
        public void open(Configuration parameters) throws Exception {
            super.open(parameters);
            System.out.println("open......");
        }

        @Override
        public void close() throws Exception {
            super.close();
            System.out.println("close......");
        }

        @Override
        public void run(SourceContext<String> ctx) throws Exception {
            ctx.collect(UUID.randomUUID().toString());
        }

        @Override
        public void cancel() {
        }
    }

}

在这里插入图片描述

基于kafka的source操作

Flink 是新一代流批统一的计算引擎,它需要从不同的第三方存储引擎中把数据读过来,进行处理,然后再写出到另外的存储引擎中。Connector 的作用就相当于一个连接器,连接 Flink 计算引擎跟外界存储系统。Flink 里有以下几种方式,当然也不限于这几种方式可以跟外界进行数据交换
在这里插入图片描述

在这里重点介绍生产环境中最常用到的 Flink kafka connector。同学们对kafka非常熟悉,它是一个分布式的、分区的、多副本的、 支持高吞吐的、发布订阅消息系统。生产环境环境中也经常会跟 kafka 进行一些数据的交换,比如利用 kafka consumer 读取数据,然后进行一系列的处理之后,再将结果写出到 kafka 中。

在这里插入图片描述

在这里插入图片描述

构造函数参数说明

  • 主题名称/主题名称列表
  • 反序列化器
  • Kafka消费者的属性。需要以下属性:
    bootstrap.servers(以逗号分隔的Kafka连接地址)
    zookeeper.connect(逗号分隔的Zookeeper服务器列表)(仅Kafka 0.8需要)
    group.id(消费者群组的ID)

反序列化Schema类型

作用:对kafka里获取的二进制数据进行反序列化
FlinkKafkaConsumer需要知道如何将kafka中的二进制数据转换成Java/Scala对象

常用的反序列化Schema

Schema说明
SimpleStringSchema按字符串方式进行序列化,反序列化
TypeInformationSerializationSchema(适合读写均是flink的场景)他们会基于Flink的TypeInformation来创建schema。这对于那些从Flink写入,又从Flink读出的数据是很有用的。这种Flink-specific的反序列化会比其他通用的序列化方式带来更高的性能。
JSONDeserializationSchema可以把序列化后的Json反序列化成ObjectNode,ObjectNode可以通过objectNode.get(“field”).as(Int/String/…)() 来访问指定的字段

Kafka Consumers消费模式配置
在取得KafkaConsumer对象后,可以设置如下方法,来配置消费模式:

消费模式描述
setStartFromGroupOffsets(默认)从kafka记录的group.id的位置开始读取,如果没有根据auto.offset.reset设置的策略
setStartFromEarliest从kafka最早的位置读取
setStartFromLatest从kafka最新数据开始读取
setStartFromTimestamp从时间戳大于或者等于指定时间戳的位置开始读取
setStartFromSpecificOffsets从指定的分区的offset位置开始读取,如指定的offsets中不存在某个分区,该分区从group offset位置开始读取

topic 和 partition 动态发现

实际的生产环境中可能有这样一些需求,比如:

  • 场景一:有一个 Flink 作业需要将五份数据聚合到一起,五份数据对应五个 kafka topic,随着业务增长,新增一类数据,同时新增了一个 kafka topic,如何在不重启作业的情况下作业自动感知新的 topic。
  • 场景二:作业从一个固定的 kafka topic 读数据,开始该 topic 有 10 个 partition,但随着业务的增长数据量变大,需要对 kafka partition 个数进行扩容,由 10 个扩容到 20。该情况下如何在不重启作业情况下动态感知新扩容的 partition?

针对上面的两种场景,首先需要在构建 FlinkKafkaConsumer 时的 properties 中设置 flink.partition-discovery.interval-millis 参数为非负值,表示开启动态发现的开关,以及设置的时间间隔 。此时 FlinkKafkaConsumer 内部会启动一个单独的线程定期去 kafka 获取最新的 meta 信息。

  • 针对场景一,还需在构建 FlinkKafkaConsumer 时,topic 的描述可以传一个正则表达式描述的 pattern。每次获取最新 kafka meta 时获取正则匹配的最新 topic 列表。
  • 针对场景二,设置前面的动态发现参数,在定期获取 kafka 最新 meta 信息时会匹配新的 partition。为了保证数据的正确性,新发现的 partition 从最早的位置开始读取。
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
Properties props = new Properties();
//指定Kafka的Broker地址
props.setProperty("bootstrap.servers","node01:9092");
//指定组ID
props.setProperty("group.id","test0311301");
//如果没有记录偏移量,第一次从最开始消费
props.setProperty("auto.offset.reset", "earliest");
//设置动态分区检测
props.setProperty("flink.partition-discovery.interval-millis", "30000");
FlinkKafkaConsumer<String> myConsumer = new FlinkKafkaConsumer<>(
                java.util.regex.Pattern.compile("test-topic-[0-9]"),
                new SimpleStringSchema(),
                props);
DataStream<String> stream = env.addSource(myConsumer);
....

使用
使用Flink流处理方式,读取Kafka的数据,并打印

开发步骤

  • 创建流处理环境
  • 指定链接kafka相关信息
  • 创建kafka数据流
  • 添加Kafka数据源
  • 打印数据
  • 执行任务

Kafka相关操作

创建topic

./bin/kafka-topics.sh --create --partitions 1 --replication-factor 1 --topic kafkatopic --zookeeper node1:2181

模拟生产者

./bin/kafka-console-producer.sh --broker-list node1:9092 --topic kafkatopic

模拟消费者

./bin/kafka-console-consumer.sh --from-beginning --topic kafkatopic --zookeeper node1:2181

测试代码

package batch.source;

import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;

import java.util.Properties;

/**
 * @author lwh
 * @date 2023/4/20
 * @description 测试kafka source
 **/
public class KafkaSourceDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        // 准备properties对象, 配置kafka相关属性
        Properties properties = new Properties();
        // Set broker
        properties.setProperty("bootstrap.servers", "node1:9092,node2:9092,node3:9092");
        // Set group id
        properties.setProperty("group.id", "flinktest");
        // Set auto offset, 如果没有offset记录就默认从最早开始读取
        properties.setProperty("auto.offset.reset", "earliest");
        // 是否自动提交偏移量
        //  properties.setProperty("enable.auto.commit", "false");
        // 这个可以通过属性设置, 不过建议通过如setStartFromEarliest这样的方法来设置更加方便和清晰, 下面代码有演示

        String topic = "kafkatopic";

        FlinkKafkaConsumer<String> kafkaConsumer = new FlinkKafkaConsumer<>(
                topic,
                new SimpleStringSchema(),
                properties
        );
            /*
            kafka的source对象可以指定从哪里开始消费
             如:
            kafkaConsumer.setStartFromEarliest();       // 从头开始消费
            kafkaConsumer.setStartFromTimestamp(System.currentTimeMillis()); // 从指定的时间戳开始消费
            kafkaConsumer.setStartFromGroupOffsets();   // 从group 中记录的offset开始消费
            kafkaConsumer.setStartFromLatest();         // 从最新开始消费

            以及指定每个从某个topic的某个分区的某个offset开始消费
            Map<KafkaTopicPartition, Long> offsets = new HashMap<>();
            offsets.put(new KafkaTopicPartition(topic, 0), 0L);
            offsets.put(new KafkaTopicPartition(topic, 1), 0L);
            offsets.put(new KafkaTopicPartition(topic, 2), 0L);
            kafkaConsumer.setStartFromSpecificOffsets(offsets);
            如上, 就指定了topic的分区0,1,2 都分别从offset 0 开始消费.
         */
        kafkaConsumer.setStartFromEarliest();   // 从最早开始消费

        // 通过KafkaConsumer对象, 得到kafka的source对象
        DataStreamSource<String> kafkaSource = env.addSource(kafkaConsumer);

        kafkaSource.print();

        env.execute("KafkaSourceDemo");


    }
}

基于mysql的source操作

上面我们已经使用了自定义数据源和Flink自带的Kafka source,那么接下来就模仿着写一个从 MySQL 中读取数据的 Source。

示例
自定义数据源, 读取MySql数据库(test)表(user)数据

idusernamepasswordname
10dazhuang123456大壮
11erya123456二丫
12sanpang123456三胖

建表语句

CREATE TABLE `user` (
  `id` int(11) NOT NULL,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;
INSERT INTO `flink`.`user`(`id`, `username`, `password`, `name`) VALUES (10, 'dazhuang', '123456', '大壮');
INSERT INTO `flink`.`user`(`id`, `username`, `password`, `name`) VALUES (11, 'erya', '123456', '二丫');
INSERT INTO `flink`.`user`(`id`, `username`, `password`, `name`) VALUES (12, 'sanpang', '123456', '三胖');
<!-- 指定mysql-connector的依赖 -->
 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>5.1.48</version>
 </dependency>

开发步骤

  • 自定义Source,继承自RichSourceFunction
  • 实现open方法
  • 实现run方法
  • 加载驱动
  • 创建连接
  • 创建PreparedStatement
  • 执行查询
  • 遍历查询结果,收集数据
  • 使用自定义Source
  • 打印结果
  • 执行任务

代码

package batch.source;

import entity.UserInfo;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.RichSourceFunction;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/**
 * @author lwh
 * @date 2023/4/20
 * @description 基于mysql的source操作
 **/
public class CustomerMysqlSourceDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        // 获得自定义Source对象
        DataStreamSource<UserInfo> mysqlSource = env.addSource(new MyMysqlSource());

        mysqlSource.print();

        env.execute("CustomerMySQLSourceDemo");

    }
    /**
     自定义Mysql Source实现类
     */
    public static class MyMysqlSource extends RichSourceFunction<UserInfo> {
        private Connection connection = null;       // 定义数据库连接对象
        private PreparedStatement ps = null;        // 定义PreparedStatement对象

        /*
        使用open方法, 这个方法在实例化类的时候会执行一次, 比较适合用来做数据库连接
         */
        @Override
        public void open(Configuration parameters) throws Exception {
            super.open(parameters);
            // 加载数据库驱动
            Class.forName("com.mysql.jdbc.Driver");

            // 创建数据库连接
            String url = "jdbc:mysql://localhost:3306/flink?useUnicode=true&characterEncoding=utf-8&useSSL=false";
            this.connection = DriverManager.getConnection(url, "root", "123456");

            // 准备PreparedStatement对象
            this.ps = connection.prepareStatement("SELECT id, username, password, name FROM user");

        }


        // 使用close方法, 这个方法在销毁实例的时候会执行一次, 比较适合用来关闭连接
        @Override
        public void close() throws Exception {
            super.close();

            // 关闭资源
            if (this.ps != null) this.ps.close();
            if (this.connection != null) this.connection.close();
        }

        @Override
        public void run(SourceContext<UserInfo> ctx) throws Exception {
            ResultSet resultSet = ps.executeQuery();
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String username = resultSet.getString("username");
                String password = resultSet.getString("password");
                String name = resultSet.getString("name");

                ctx.collect(new UserInfo(id, username, password, name));
            }
        }

        @Override
        public void cancel() {
            System.out.println("任务被取消......");
        }
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/452150.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【A component required a bean of type ‘xxx‘ that could not be found.】

在学谷粒商城项目的时候出现了以下问题&#xff1a; *************************** APPLICATION FAILED TO START *************************** Description: A component required a bean of type org.redisson.Redisson that could not be found. Action: Consider defining a…

React-Router详解

React-Router详解 简介React-Router React-Router是一款用于构建单页面应用&#xff08;SPA&#xff09;中处理路由的JavaScript库。在现代的Web应用中&#xff0c;SPA已经成为了一种常见的应用架构模式&#xff0c;它允许在不刷新整个页面的情况下进行交互式的用户体验。而Re…

GPT能给审计带来什么

ChatGPT的出现&#xff0c;让人工智能再次站在了聚光灯下&#xff0c;引发持续性的热议和关注。GPT模型作为重要的支撑&#xff0c;国内外近段时间密集性地发布了众多的大语言模型&#xff0c;OpenAI推出GPT-4、谷歌推出LaMDA和PaLM等大模型、Meta推出开源大模型LLaMA&#xff…

2023-04-23 学习记录--C/C++-邂逅C/C++(中)

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 上一篇&#xff1a;邂逅C/C&#xff08;上&#xff09; 一、if语句 ⭐️ &#xff08;一&#xff09;、格式 &#x1f36d; if…

6.1 欧拉方法与改进欧拉方法

6.1.1 欧拉方法 欧拉方法是一种数值解常微分方程&#xff08;ODE&#xff09;的方法&#xff0c;可以用于近似求解给定的初值问题。它是以欧拉命名的瑞士数学家莱昂哈德欧拉所发明的&#xff0c;因此得名。 欧拉方法的基本思路是将连续的常微分方程转化为离散的形式。具体而言…

全功能药效团图谱(Full-feature pharmacophore map)是什么?怎么绘制?

药效团和全功能药效团图谱是什么&#xff1f; 药效团是指与靶点相互作用并导致生物活性的化学基团或者分子结构片段。【药效团通常包括氢键受体/供体、疏水部位、离子化部位等关键成分】 "全功能药效团图谱" &#xff08;Full-feature pharmacophore map&#xff0…

.net6 core web项目发布部署到Linux,以守护进程服务的形式部署启动,nginx实现转发

一、发布项目 1、以文件夹形式 2、目标运行时选对应的平台&#xff08;Linux-x64&#xff09; 3、文件夹选项&#xff1a;在发布前删除所有现有文件 二、部署项目&#xff08;安装.net6环境&#xff1a;参考Linux安装 dotnet sdk 6.0&#xff09; &#xff08;1&#xff09;…

toRef

toRef就是把什么东西变成ref 用这个页面来讲解 改成这样就用不了&#xff0c;失去了响应的功能 为什么会这样&#xff1f; 看这个页面&#xff0c;其实这个Proxy的实现就响应式的原理&#xff0c;但是let namep.name实际上就是let name张三&#xff0c;只是把这个值给了name&…

Android 日志框架使用

在实际开发中&#xff0c;经常会遇到需要打印日志并保存到文件中&#xff0c;便于后面取日志分析代码运行情况&#xff0c;当然如果只是打印日志不需要记录文件&#xff0c;使用android自带的log工具就完全够了&#xff0c; Log打印日志会记录到系统日志中&#xff0c;可以取出…

光敏电阻传感器模块资料

实物项目案例 实物图&#xff1a; PCB图&#xff1a; 原理图&#xff1a; 用途&#xff1a; 光线亮度检测,光线亮度传感器&#xff0c;智能小车寻光模块 模块特色&#xff1a; 1、采用灵敏型光敏电阻传感器 2、比较器输出&#xff0c;信号干净&#xff0c;波形好&#x…

【Leetcode】10. 正则表达式匹配

10. 正则表达式匹配&#xff08;困难&#xff09; 题解 如果从左向右进行匹配的话&#xff0c;需要考虑字符后是否有 * 。 因此选择从右向左扫描更为简单。 *前面肯定有一个字符&#xff0c;它像是一个拷贝器&#xff0c;能够复制前面的单个字符&#xff0c;甚至也可以把这个…

Java 1.8新特性

接口的默认方法 Java 8允许给接口添加一个非抽象的方法实现&#xff0c;只需要使用default关键字即可&#xff0c;这个特征又叫做扩展方法 interface Formula {double calculate(int a);default double sqrt(int a) {return Math.sqrt(a);} }Formula接口在拥有calculate方法之…

【Linux初阶】进程的相关概念 | 进程管理 查看进程 获取进程标识符 fork进程创建

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;进程的概念&#xff0c;进程管理初识&#xff08;描述、管理进程&#xff09;&#xff0c;查看进程的基础方法…

【Nav2】ROS2 Eloquent中使用robot_state_publisher发布松灵Scout mini的urdf

【背景】 本来打算把ROS1版本的松灵Scout mini 模型描述文件直接移植到ROS2的工作空间里去&#xff0c;用robot_state_publisher加载出来&#xff0c;结果行不通&#xff1b;于是找到了ROS2版本的Scout mini 模型描述&#xff0c;结果因为我用的是 Eloquent 版本&#xff0c;而…

MobTech MobPush|A/B测试提升运营决策

在实际推送过程中&#xff0c;我们常常有这样的困惑&#xff1a; 我们如何确定哪种推送内容更能吸引用户&#xff1f; 我们如何衡量推送效果的提升程度&#xff1f; 我们如何优化推送方案&#xff0c;实现更高的ROI&#xff1f; 为了解决这些困惑&#xff0c;我们需要一种科…

碳中和成“必答题”,宁德时代创造产业“零碳”新维度

文 | 智能相对论 作者 | leo陈 2021年&#xff0c;麻省理工学院的教授索尔格里菲斯出版了《零碳未来》一书&#xff0c;这本著作总结了数十年的研究经验和数据&#xff0c;紧迫地提出&#xff0c;我们需要可以利用现有技术和资源快速应对气候变化的“零碳行动方案”。 既要保…

Netty时间轮源码解析

Netty主要应用用于网络通信&#xff0c;Netty还有一个非常重要的应用领域&#xff0c;即时通信系统IM, 在IM聊天系统中&#xff0c;有成千上万条条链路&#xff0c; Netty是如何管理这些链路的呢 &#xff1f; Netty还有一套自带的心跳检测机制&#xff0c;这套检测机制的原理是…

HCIP-7.0华为交换机技术基础学习

交换机基础 1、交换机工作原理1.1、VLAN虚拟局域网1.1.1、VLAN帧格式1.1.2、VLAN的划分方式&#xff1a;1.1.3、PVID1.1.4、Access端口类型1.1.5、Trunk端口类型1.1.6、Hybird端口类型 1.2、MUX VLAN应用场景和原理1.2.1、MUX VLAN原理1.2.2、MUX VLAN配置 1、交换机工作原理 …

软件测试—用例篇(上)

软件测试—用例篇 &#x1f50e;测试用例的概念&#x1f50e;测试用例的好处&#x1f50e;测试用例的设计方法&#x1f338;基于需求的设计方法&#x1f338;等价类划分法&#x1f338;边界值分析法&#x1f338;判定表 &#x1f50e;结尾 &#x1f50e;测试用例的概念 为了实…

【Python】如何用pyth做游戏脚本(太简单了吧)

文章目录 前言一、开发前景二、开发流程3.1、获取窗口句柄&#xff0c;把窗口置顶3. 2、截取游戏界面&#xff0c;分割图标&#xff0c;图片比较 二、程序核心-图标连接算法&#xff08;路径寻找&#xff09;四、开发总结五、源码总结 前言 简述&#xff1a;本文将以4399小游戏…