Flink教程(06)- Flink批流一体API(Source示例)

01 引言

在前面的博客,我们已经对Flink的原理有了一定的了解了,有兴趣的同学可以参阅下:

本文开始讲解Flink程序模型对应的代码,也就是Flink批流一体对应的API,分别对应为:SourceTransformationSlink,本文讲Source

在这里插入图片描述

02 Source

Source对应的就是Flink编程模型里面的Data Source数据源:
在这里插入图片描述

Flink官网,我们可以知道Source有如下几种类型:
在这里插入图片描述
转义为中文即:

  • File-based:基于文件的的Source
  • Socket-based: 基于Socket的Source
  • Collection-based: 基于集合的Source
  • Custom: 自定义Source

2.1 基于集合的Source

相关API(一般用于学习测试时编造数据时使用):

  • env.fromElements(可变参数);
  • env.fromColletion(各种集合);
  • env.generateSequence(开始,结束);
  • env.fromSequence(开始,结束)。

示例代码:

/**
 * 把本地的普通的Java集合/Scala集合变为分布式的Flink的DataStream集合!
 *
 * @author : YangLinWei
 * @createTime: 2022/3/7 2:55 下午
 * <p>
 * 1.env.fromElements(可变参数);
 * 2.env.fromColletion(各种集合);
 * 3.env.generateSequence(开始,结束);
 * 4.env.fromSequence(开始,结束);
 */
public class SourceDemo1 {

    public static void main(String[] args) throws Exception {

        //1.env
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        //2.source
        // * 1.env.fromElements(可变参数);
        DataStream<String> ds1 = env.fromElements("hadoop", "spark", "flink");
        // * 2.env.fromColletion(各种集合);
        DataStream<String> ds2 = env.fromCollection(Arrays.asList("hadoop", "spark", "flink"));
        // * 3.env.generateSequence(开始,结束);
        DataStream<Long> ds3 = env.generateSequence(1, 10);
        //* 4.env.fromSequence(开始,结束);
        DataStream<Long> ds4 = env.fromSequence(1, 10);

        //3.Transformation

        //4.sink
        ds1.print();
        ds2.print();
        ds3.print();
        ds4.print();

        //5.execute
        env.execute();
    }
}

运行结果:
在这里插入图片描述

2.2 基于文件的Source

相关API(一般用于学习测试):

  • env.readTextFile(本地/HDFS文件/文件夹/压缩文件)

示例代码:

/**
 * env.readTextFile(本地/HDFS文件/文件夹/压缩文件)
 *
 * @author : YangLinWei
 * @createTime: 2022/3/7 2:59 下午
 */
public class SourceDemo2 {

    public static void main(String[] args) throws Exception {
        //1.env
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        //2.source
        // * 1.env.readTextFile(本地文件/HDFS文件);//压缩文件也可以
        DataStream<String> ds1 = env.readTextFile("data/input/words.txt");
        DataStream<String> ds2 = env.readTextFile("data/input/dir");
        DataStream<String> ds3 = env.readTextFile("hdfs://node1:8020//wordcount/input/words.txt");
        DataStream<String> ds4 = env.readTextFile("data/input/wordcount.txt.gz");

        //3.Transformation

        //4.sink
        ds1.print();
        ds2.print();
        ds3.print();
        ds4.print();

        //5.execute
        env.execute();
    }
}

2.3 基于Socket的Source

需求:在node1上使用nc -lk 9999 向指定端口发送数据(ncnetcat的简称,原本是用来设置路由器,我们可以利用它向某个端口发送数据),如果没有该命令可以下安装:

yum install -y nc

使用Flink编写流处理应用程序实时统计单词数量,代码如下:

/**
 * SocketSource
 *
 * @author : YangLinWei
 * @createTime: 2022/3/7 3:02 下午
 */
public class SourceDemo3 {

    public static void main(String[] args) throws Exception {

        //1.env
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        //2.source
        DataStream<String> linesDS = env.socketTextStream("node1", 9999);

        //3.处理数据-transformation
        //|_____3.1每一行数据按照空格切分成一个个的单词组成一个集合
        DataStream<String> wordsDS = linesDS.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public void flatMap(String value, Collector<String> out) throws Exception {
                //value就是一行行的数据
                String[] words = value.split(" ");
                for (String word : words) {
                    out.collect(word);//将切割处理的一个个的单词收集起来并返回
                }
            }
        });
        //|_____3.2对集合中的每个单词记为1
        DataStream<Tuple2<String, Integer>> wordAndOnesDS = wordsDS.map(new MapFunction<String, Tuple2<String, Integer>>() {
            @Override
            public Tuple2<String, Integer> map(String value) throws Exception {
                //value就是进来一个个的单词
                return Tuple2.of(value, 1);
            }
        });
        //|_____3.3对数据按照单词(key)进行分组
        //KeyedStream<Tuple2<String, Integer>, Tuple> groupedDS = wordAndOnesDS.keyBy(0);
        KeyedStream<Tuple2<String, Integer>, String> groupedDS = wordAndOnesDS.keyBy(t -> t.f0);
        //|_____3.4对各个组内的数据按照数量(value)进行聚合就是求sum
        DataStream<Tuple2<String, Integer>> result = groupedDS.sum(1);

        //4.输出结果-sink
        result.print();

        //5.触发执行-execute
        env.execute();
    }
}

2.4 自定义Source

2.4.1 案例 - 随机生成数据

Flink还提供了数据源接口,我们实现该接口就可以实现自定义数据源,不同的接口有不同的功能,分类如下:

  • SourceFunction:非并行数据源(并行度只能=1)
  • RichSourceFunction:多功能非并行数据源(并行度只能=1)
  • ParallelSourceFunction:并行数据源(并行度能够>=1)
  • RichParallelSourceFunction:多功能并行数据源(并行度能够>=1) ,Kafka数据源使用的就是该接口。

需求:每隔1秒随机生成一条订单信息(订单ID、用户ID、订单金额、时间戳)
要求:

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

示例代码:

/**
 * 自定义Source
 *
 * @author : YangLinWei
 * @createTime: 2022/3/7 3:08 下午
 * Flink还提供了数据源接口,我们实现该接口就可以实现自定义数据源,不同的接口有不同的功能,分类如下:
 * SourceFunction:非并行数据源(并行度只能=1)
 * RichSourceFunction:多功能非并行数据源(并行度只能=1)
 * ParallelSourceFunction:并行数据源(并行度能够>=1)
 * RichParallelSourceFunction:多功能并行数据源(并行度能够>=1)--后续学习的Kafka数据源使用的就是该接口
 */
public class SourceDemo4 {


    public static void main(String[] args) throws Exception {

        //1.env
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        //2.Source
        DataStream<Order> orderDS = env
                .addSource(new MyOrderSource())
                .setParallelism(2);

        //3.Transformation

        //4.Sink
        orderDS.print();
        //5.execute
        env.execute();
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Order {
        private String id;
        private Integer userId;
        private Integer money;
        private Long createTime;
    }

    public static class MyOrderSource extends RichParallelSourceFunction<Order> {
        private Boolean flag = true;

        @Override
        public void run(SourceContext<Order> ctx) throws Exception {
            Random random = new Random();
            while (flag) {
                Thread.sleep(1000);
                String id = UUID.randomUUID().toString();
                int userId = random.nextInt(3);
                int money = random.nextInt(101);
                long createTime = System.currentTimeMillis();
                ctx.collect(new Order(id, userId, money, createTime));
            }
        }

        //取消任务/执行cancle命令的时候执行
        @Override
        public void cancel() {
            flag = false;
        }
    }
}

运行结果如下:
在这里插入图片描述

2.4.2 案例 - MySQL

需求:实际开发中,经常会实时接收一些数据,要和MySQL中存储的一些规则进行匹配,那么这时候就可以使用Flink自定义数据源从MySQL中读取数据。

那么现在先完成一个简单的需求:

  • MySQL中实时加载数据;
  • 要求MySQL中的数据有变化,也能被实时加载出来。

首先准备数据:

CREATE TABLE `t_student` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `name` varchar(255) DEFAULT NULL,
    `age` int(11) DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

INSERT INTO `t_student` VALUES ('1', 'jack', '18');
INSERT INTO `t_student` VALUES ('2', 'tom', '19');
INSERT INTO `t_student` VALUES ('3', 'rose', '20');
INSERT INTO `t_student` VALUES ('4', 'tom', '19');
INSERT INTO `t_student` VALUES ('5', 'jack', '18');
INSERT INTO `t_student` VALUES ('6', 'rose', '20');

代码实现如下:


/**
 * 简单的需求:
 * 从MySQL中实时加载数据
 * 要求MySQL中的数据有变化,也能被实时加载出来
 *
 * @author : YangLinWei
 * @createTime: 2022/3/7 3:17 下午
 */
public class SourceDemo5 {

    public static void main(String[] args) throws Exception {
        //1.env
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        //2.Source
        DataStream<Student> studentDS = env.addSource(new MySQLSource()).setParallelism(1);

        //3.Transformation
        //4.Sink
        studentDS.print();

        //5.execute
        env.execute();
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class Student {
        private Integer id;
        private String name;
        private Integer age;
    }

    public static class MySQLSource extends RichParallelSourceFunction<Student> {
        private Connection conn = null;
        private PreparedStatement ps = null;

        @Override
        public void open(Configuration parameters) throws Exception {
            //加载驱动,开启连接
            //Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/big_data", "root", "123456");
            String sql = "select id,name,age from t_student";
            ps = conn.prepareStatement(sql);
        }

        private boolean flag = true;

        @Override
        public void run(SourceContext<Student> ctx) throws Exception {
            while (flag) {
                ResultSet rs = ps.executeQuery();
                while (rs.next()) {
                    int id = rs.getInt("id");
                    String name = rs.getString("name");
                    int age = rs.getInt("age");
                    ctx.collect(new Student(id, name, age));
                }
                TimeUnit.SECONDS.sleep(5);
            }
        }

        @Override
        public void cancel() {
            flag = false;
        }

        @Override
        public void close() throws Exception {
            if (conn != null) conn.close();
            if (ps != null) ps.close();
        }
    }
}

运行结果:
在这里插入图片描述

03 文末

本文主要讲解Flink批流一体API中的Source用法,谢谢大家的阅读,本文完!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
下面是使用 Flink Connector TiDB CDC 通过 Stream API 连接 TiDB 的示例代码: ```java import org.apache.flink.api.common.functions.MapFunction; import org.apache.flink.api.common.serialization.SimpleStringSchema; import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer; import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer; import org.apache.flink.streaming.connectors.tidb.JdbcConnectionOptions; import org.apache.flink.streaming.connectors.tidb.TiDBOptions; import org.apache.flink.streaming.connectors.tidb.TiDBSink; import org.apache.flink.streaming.connectors.tidb.TiDBSource; import org.apache.flink.streaming.connectors.tidb.TransactionIsolation; import org.apache.flink.streaming.connectors.tidb.TiDBCatalog; import org.apache.flink.table.api.EnvironmentSettings; import org.apache.flink.table.api.bridge.java.StreamTableEnvironment; import org.apache.flink.table.api.bridge.java.internal.StreamTableEnvironmentImpl; import org.apache.flink.types.Row; import java.util.Properties; public class TiDBStreamExample { public static void main(String[] args) throws Exception { StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build(); StreamTableEnvironment tEnv = StreamTableEnvironment.create(env, settings); // Define TiDB catalog TiDBCatalog catalog = new TiDBCatalog("tidb_catalog", "default_database", JdbcConnectionOptions.builder() .withUrl("jdbc:mysql://tidb_ip:tidb_port/tidb_database_name?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC") .withUsername("tidb_username") .withPassword("tidb_password") .build(), TiDBOptions.builder().withDatabaseUrl("jdbc:mysql://tidb_ip:tidb_port/tidb_database_name").build()); tEnv.registerCatalog("tidb_catalog", catalog); tEnv.useCatalog("tidb_catalog"); // Define TiDB source TiDBSource source = TiDBSource.builder() .setDatabaseName("tidb_database_name") .setTableName("tidb_table_name") .setOptions(TiDBOptions.builder() .withDatabaseUrl("jdbc:mysql://tidb_ip:tidb_port/tidb_database_name") .build()) .setPrimaryKey("id") .setTransactionIsolation(TransactionIsolation.READ_COMMITTED) .build(); // Create a data stream from TiDB source DataStream<Row> stream = env.addSource(source); // Define Flink Kafka producer Properties props = new Properties(); props.setProperty("bootstrap.servers", "kafka_ip:kafka_port"); FlinkKafkaProducer<String> kafkaProducer = new FlinkKafkaProducer<String>( "kafka_topic", new SimpleStringSchema(), props); // Map the data stream to a string stream and send it to Kafka DataStream<String> stringStream = stream.map(new MapFunction<Row, String>() { @Override public String map(Row row) throws Exception { return row.toString(); } }); stringStream.addSink(kafkaProducer); // Define Flink Kafka consumer FlinkKafkaConsumer<String> kafkaConsumer = new FlinkKafkaConsumer<String>( "kafka_topic", new SimpleStringSchema(), props); // Create a data stream from Kafka DataStream<String> kafkaStream = env.addSource(kafkaConsumer); // Convert the Kafka stream to a table and register it in the table environment tEnv.createTemporaryView("kafka_table", kafkaStream, "value"); // Query the table and print the result to console tEnv.sqlQuery("SELECT * FROM kafka_table").execute().print(); // Define TiDB sink TiDBSink sink = TiDBSink.builder() .setDatabaseName("tidb_database_name") .setTableName("tidb_table_name") .setOptions(TiDBOptions.builder() .withDatabaseUrl("jdbc:mysql://tidb_ip:tidb_port/tidb_database_name") .build()) .setPrimaryKey("id") .build(); // Convert the Kafka stream back to a data stream of rows and write it to TiDB DataStream<Row> rowStream = kafkaStream.map(new MapFunction<String, Row>() { @Override public Row map(String value) throws Exception { String[] fields = value.split(","); return Row.of(Integer.parseInt(fields[0]), fields[1], Double.parseDouble(fields[2])); } }); rowStream.addSink(sink); env.execute("TiDB Stream Example"); } } ``` 在上面的示例代码中,我们首先定义了一个 TiDBCatalog 对象,用于连接 TiDB 数据库。然后,我们使用 TiDBSource.builder() 方法定义了一个 TiDB 数据源,用于从 TiDB 中读取数据。接着,我们使用 env.addSource(source) 方法创建了一个 Flink 数据流。我们还定义了一个 Flink Kafka 生产者,用于将数据流发送到 Kafka。为了将数据流转换为字符串流,我们使用了 map() 方法。然后,我们将字符串流发送到 Kafka。接着,我们定义了一个 Flink Kafka 消费者,用于从 Kafka 中读取数据。我们还将 Kafka 数据流转换为表,并在表环境中注册它。最后,我们使用 TiDBSink.builder() 方法定义了一个 TiDB 汇聚器,用于将数据流写入 TiDB 中。 请注意,在上面的示例代码中,我们使用了 TiDBCatalog 和 TiDBSource 类来连接 TiDB 数据库。这些类需要 TiDB Connector JAR 包的支持。如果您没有安装该 JAR 包,请按照以下步骤安装: 1. 访问 TiDB Connector JAR 包的下载页面:https://github.com/pingcap/tidb/releases/tag/v4.0.8 2. 下载适用于您的操作系统的 JAR 包 3. 将 JAR 包添加到您的项目依赖中 最后,记得将代码中的 tidb_ip、tidb_port、tidb_database_name、tidb_table_name、tidb_username 和 tidb_password 替换为实际的值。同样,将代码中的 kafka_ip、kafka_port 和 kafka_topic 替换为实际的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值