《从0到1学习Flink》—— 如何自定义 Data Source ?

前言

《从0到1学习Flink》—— Data Source 介绍 文章中,我给大家介绍了 Flink Data Source 以及简短的介绍了一下自定义 Data Source,这篇文章更详细的介绍下,并写一个 demo 出来让大家理解。

Flink Kafka source

准备工作

我们先来看下 Flink 从 Kafka topic 中获取数据的 demo,首先你需要安装好了 FLink 和 Kafka 。

运行启动 Flink、Zookepeer、Kafka,

好了,都启动了!

maven 依赖

 1<!--flink java-->
 2<dependency>
 3    <groupId>org.apache.flink</groupId>
 4    <artifactId>flink-java</artifactId>
 5    <version>${flink.version}</version>
 6    <scope>provided</scope>
 7</dependency>
 8<dependency>
 9    <groupId>org.apache.flink</groupId>
10    <artifactId>flink-streaming-java_${scala.binary.version}</artifactId>
11    <version>${flink.version}</version>
12    <scope>provided</scope>
13</dependency>
14<!--日志-->
15<dependency>
16    <groupId>org.slf4j</groupId>
17    <artifactId>slf4j-log4j12</artifactId>
18    <version>1.7.7</version>
19    <scope>runtime</scope>
20</dependency>
21<dependency>
22    <groupId>log4j</groupId>
23    <artifactId>log4j</artifactId>
24    <version>1.2.17</version>
25    <scope>runtime</scope>
26</dependency>
27<!--flink kafka connector-->
28<dependency>
29    <groupId>org.apache.flink</groupId>
30    <artifactId>flink-connector-kafka-0.11_${scala.binary.version}</artifactId>
31    <version>${flink.version}</version>
32</dependency>
33<!--alibaba fastjson-->
34<dependency>
35    <groupId>com.alibaba</groupId>
36    <artifactId>fastjson</artifactId>
37    <version>1.2.51</version>
38</dependency>

测试发送数据到 kafka topic

实体类,Metric.java

 1package com.zhisheng.flink.model;
 2
 3import java.util.Map;
 4
 5/**
 6 * Desc:
 7 * weixi: zhisheng_tian
 8 * blog: http://www.54tianzhisheng.cn/
 9 */
10public class Metric {
11    public String name;
12    public long timestamp;
13    public Map<String, Object> fields;
14    public Map<String, String> tags;
15
16    public Metric() {
17    }
18
19    public Metric(String name, long timestamp, Map<String, Object> fields, Map<String, String> tags) {
20        this.name = name;
21        this.timestamp = timestamp;
22        this.fields = fields;
23        this.tags = tags;
24    }
25
26    @Override
27    public String toString() {
28        return "Metric{" +
29                "name='" + name + '\'' +
30                ", timestamp='" + timestamp + '\'' +
31                ", fields=" + fields +
32                ", tags=" + tags +
33                '}';
34    }
35
36    public String getName() {
37        return name;
38    }
39
40    public void setName(String name) {
41        this.name = name;
42    }
43
44    public long getTimestamp() {
45        return timestamp;
46    }
47
48    public void setTimestamp(long timestamp) {
49        this.timestamp = timestamp;
50    }
51
52    public Map<String, Object> getFields() {
53        return fields;
54    }
55
56    public void setFields(Map<String, Object> fields) {
57        this.fields = fields;
58    }
59
60    public Map<String, String> getTags() {
61        return tags;
62    }
63
64    public void setTags(Map<String, String> tags) {
65        this.tags = tags;
66    }
67}

往 kafka 中写数据工具类:KafkaUtils.java

 1import com.alibaba.fastjson.JSON;
 2import com.zhisheng.flink.model.Metric;
 3import org.apache.kafka.clients.producer.KafkaProducer;
 4import org.apache.kafka.clients.producer.ProducerRecord;
 5
 6import java.util.HashMap;
 7import java.util.Map;
 8import java.util.Properties;
 9
10/**
11 * 往kafka中写数据
12 * 可以使用这个main函数进行测试一下
13 * weixin: zhisheng_tian 
14 * blog: http://www.54tianzhisheng.cn/
15 */
16public class KafkaUtils {
17    public static final String broker_list = "localhost:9092";
18    public static final String topic = "metric";  // kafka topic,Flink 程序中需要和这个统一 
19
20    public static void writeToKafka() throws InterruptedException {
21        Properties props = new Properties();
22        props.put("bootstrap.servers", broker_list);
23        props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); //key 序列化
24        props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer"); //value 序列化
25        KafkaProducer producer = new KafkaProducer<String, String>(props);
26
27        Metric metric = new Metric();
28        metric.setTimestamp(System.currentTimeMillis());
29        metric.setName("mem");
30        Map<String, String> tags = new HashMap<>();
31        Map<String, Object> fields = new HashMap<>();
32
33        tags.put("cluster", "zhisheng");
34        tags.put("host_ip", "101.147.022.106");
35
36        fields.put("used_percent", 90d);
37        fields.put("max", 27244873d);
38        fields.put("used", 17244873d);
39        fields.put("init", 27244873d);
40
41        metric.setTags(tags);
42        metric.setFields(fields);
43
44        ProducerRecord record = new ProducerRecord<String, String>(topic, null, null, JSON.toJSONString(metric));
45        producer.send(record);
46        System.out.println("发送数据: " + JSON.toJSONString(metric));
47
48        producer.flush();
49    }
50
51    public static void main(String[] args) throws InterruptedException {
52        while (true) {
53            Thread.sleep(300);
54            writeToKafka();
55        }
56    }
57}

运行:

如果出现如上图标记的,即代表能够不断的往 kafka 发送数据的。

Flink 程序

Main.java

 1package com.zhisheng.flink;
 2
 3import org.apache.flink.api.common.serialization.SimpleStringSchema;
 4import org.apache.flink.streaming.api.datastream.DataStreamSource;
 5import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
 6import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer011;
 7
 8import java.util.Properties;
 9
10/**
11 * Desc:
12 * weixi: zhisheng_tian
13 * blog: http://www.54tianzhisheng.cn/
14 */
15public class Main {
16    public static void main(String[] args) throws Exception {
17        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
18
19        Properties props = new Properties();
20        props.put("bootstrap.servers", "localhost:9092");
21        props.put("zookeeper.connect", "localhost:2181");
22        props.put("group.id", "metric-group");
23        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");  //key 反序列化
24        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
25        props.put("auto.offset.reset", "latest"); //value 反序列化
26
27        DataStreamSource<String> dataStreamSource = env.addSource(new FlinkKafkaConsumer011<>(
28                "metric",  //kafka topic
29                new SimpleStringSchema(),  // String 序列化
30                props)).setParallelism(1);
31
32        dataStreamSource.print(); //把从 kafka 读取到的数据打印在控制台
33
34        env.execute("Flink add data source");
35    }
36}

运行起来:

看到没程序,Flink 程序控制台能够源源不断的打印数据呢。

自定义 Source

上面就是 Flink 自带的 Kafka source,那么接下来就模仿着写一个从 MySQL 中读取数据的 Source。

首先 pom.xml 中添加 MySQL 依赖

1<dependency>
2    <groupId>mysql</groupId>
3    <artifactId>mysql-connector-java</artifactId>
4    <version>5.1.34</version>
5</dependency>

数据库建表如下:

1DROP TABLE IF EXISTS `student`;
2CREATE TABLE `student` (
3  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
4  `name` varchar(25) COLLATE utf8_bin DEFAULT NULL,
5  `password` varchar(25) COLLATE utf8_bin DEFAULT NULL,
6  `age` int(10) DEFAULT NULL,
7  PRIMARY KEY (`id`)
8) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

插入数据

1INSERT INTO `student` VALUES ('1', 'zhisheng01', '123456', '18'), ('2', 'zhisheng02', '123', '17'), ('3', 'zhisheng03', '1234', '18'), ('4', 'zhisheng04', '12345', '16');
2COMMIT;

新建实体类:Student.java

 1package com.zhisheng.flink.model;
 2
 3/**
 4 * Desc:
 5 * weixi: zhisheng_tian
 6 * blog: http://www.54tianzhisheng.cn/
 7 */
 8public class Student {
 9    public int id;
10    public String name;
11    public String password;
12    public int age;
13
14    public Student() {
15    }
16
17    public Student(int id, String name, String password, int age) {
18        this.id = id;
19        this.name = name;
20        this.password = password;
21        this.age = age;
22    }
23
24    @Override
25    public String toString() {
26        return "Student{" +
27                "id=" + id +
28                ", name='" + name + '\'' +
29                ", password='" + password + '\'' +
30                ", age=" + age +
31                '}';
32    }
33
34    public int getId() {
35        return id;
36    }
37
38    public void setId(int id) {
39        this.id = id;
40    }
41
42    public String getName() {
43        return name;
44    }
45
46    public void setName(String name) {
47        this.name = name;
48    }
49
50    public String getPassword() {
51        return password;
52    }
53
54    public void setPassword(String password) {
55        this.password = password;
56    }
57
58    public int getAge() {
59        return age;
60    }
61
62    public void setAge(int age) {
63        this.age = age;
64    }
65}

新建 Source 类 SourceFromMySQL.java,该类继承 RichSourceFunction ,实现里面的 open、close、run、cancel 方法:

 1 package com.zhisheng.flink.source;
 2
 3import com.zhisheng.flink.model.Student;
 4import org.apache.flink.configuration.Configuration;
 5import org.apache.flink.streaming.api.functions.source.RichSourceFunction;
 6
 7import java.sql.Connection;
 8import java.sql.DriverManager;
 9import java.sql.PreparedStatement;
10import java.sql.ResultSet;
11
12
13/**
14 * Desc:
15 * weixi: zhisheng_tian
16 * blog: http://www.54tianzhisheng.cn/
17 */
18public class SourceFromMySQL extends RichSourceFunction<Student> {
19
20    PreparedStatement ps;
21    private Connection connection;
22
23    /**
24     * open() 方法中建立连接,这样不用每次 invoke 的时候都要建立连接和释放连接。
25     *
26     * @param parameters
27     * @throws Exception
28     */
29    @Override
30    public void open(Configuration parameters) throws Exception {
31        super.open(parameters);
32        connection = getConnection();
33        String sql = "select * from Student;";
34        ps = this.connection.prepareStatement(sql);
35    }
36
37    /**
38     * 程序执行完毕就可以进行,关闭连接和释放资源的动作了
39     *
40     * @throws Exception
41     */
42    @Override
43    public void close() throws Exception {
44        super.close();
45        if (connection != null) { //关闭连接和释放资源
46            connection.close();
47        }
48        if (ps != null) {
49            ps.close();
50        }
51    }
52
53    /**
54     * DataStream 调用一次 run() 方法用来获取数据
55     *
56     * @param ctx
57     * @throws Exception
58     */
59    @Override
60    public void run(SourceContext<Student> ctx) throws Exception {
61        ResultSet resultSet = ps.executeQuery();
62        while (resultSet.next()) {
63            Student student = new Student(
64                    resultSet.getInt("id"),
65                    resultSet.getString("name").trim(),
66                    resultSet.getString("password").trim(),
67                    resultSet.getInt("age"));
68            ctx.collect(student);
69        }
70    }
71
72    @Override
73    public void cancel() {
74    }
75
76    private static Connection getConnection() {
77        Connection con = null;
78            try {
79                Class.forName("com.mysql.jdbc.Driver");
80                con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8", "root", "root123456");
81            } catch (Exception e) {
82                System.out.println("-----------mysql get connection has exception , msg = "+ e.getMessage());
83            }
84        return con;
85    }
86}

Flink 程序

 1package com.zhisheng.flink;
 2
 3import com.zhisheng.flink.source.SourceFromMySQL;
 4import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
 5
 6/**
 7 * Desc:
 8 * weixi: zhisheng_tian
 9 * blog: http://www.54tianzhisheng.cn/
10 */
11public class Main2 {
12    public static void main(String[] args) throws Exception {
13        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
14
15        env.addSource(new SourceFromMySQL()).print();
16
17        env.execute("Flink add data sourc");
18    }
19}

运行 Flink 程序,控制台日志中可以看见打印的 student 信息。

RichSourceFunction

从上面自定义的 Source 可以看到我们继承的就是这个 RichSourceFunction 类,那么来了解一下:

一个抽象类,继承自 AbstractRichFunction。为实现一个 Rich SourceFunction 提供基础能力。该类的子类有三个,两个是抽象类,在此基础上提供了更具体的实现,另一个是 ContinuousFileMonitoringFunction。

  • MessageAcknowledgingSourceBase :它针对的是数据源是消息队列的场景并且提供了基于 ID 的应答机制。
  • MultipleIdsMessageAcknowledgingSourceBase : 在 MessageAcknowledgingSourceBase 的基础上针对 ID 应答机制进行了更为细分的处理,支持两种 ID 应答模型:session id 和 unique message id。
  • ContinuousFileMonitoringFunction:这是单个(非并行)监视任务,它接受 FileInputFormat,并且根据 FileProcessingMode 和 FilePathFilter,它负责监视用户提供的路径;决定应该进一步读取和处理哪些文件;创建与这些文件对应的 FileInputSplit 拆分,将它们分配给下游任务以进行进一步处理。

最后

本文主要讲了下 Flink 使用 Kafka Source 的使用,并提供了一个 demo 教大家如何自定义 Source,从 MySQL 中读取数据,当然你也可以从其他地方读取,实现自己的数据源 source。可能平时工作会比这个更复杂,需要大家灵活应对!

关注我

转载请务必注明原创地址为:http://www.54tianzhisheng.cn/2018/10/30/flink-create-source/

原文发布于微信公众号 - zhisheng(zhisheng_blog)

原文发表时间:2018-11-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java3y

阅读SSM项目之scm

导入项目 项目是由eclipse来编写的,我使用的开发环境是Idea,那么就需要将eclipse项目导入进去Idea中。要想项目能够启动起来。是这样干的: 导入...

35011
来自专栏Jerry的SAP技术分享

ABAP和Java单例模式的攻防

然而我只需要将这个单例类JerrySingleton的构造函数通过反射设置成可以访问Accessible,然后就能通过反射调用该构造函数,进而生成新的对象实例。...

1794
来自专栏nice_每一天

Elasticsearch JavaApi

 官网JavaApi地址:https://www.elastic.co/guide/en/elasticsearch/client/java-api/curre...

7124
来自专栏沃趣科技

MySQL排序内部原理探秘

一、我们要解决什么问题 二、排序,排序,排序 三、索引优化排序 四、排序模式 4.1实际trace结果 4.2排序模式概览 4.2.1回表排序模式 4.2.2不...

5066
来自专栏python学习路

三、模型(一)

当我们的程序涉及到数据库相关操作时,我们一般都会这么做: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去调...

4109
来自专栏数据结构与算法

HDU6315 Naive Operations(线段树 复杂度分析)

设\(d_i\)表示\(i\)号节点还需要加\(d_i\)次才能产生\(1\)的贡献

1254
来自专栏Java帮帮-微信公众号-技术文章全总结

第二十九天-加强1-Junit&类加载&反射&Properties&BeanUtils&xml&动态代理&数据库【悟空教程】

第二十九天-加强1-Junit&类加载&反射&Properties&BeanUtils&xml&动态代理&数据库【悟空教程】

2257
来自专栏一个会写诗的程序员的博客

spring boot 集成mybatis 注解版查询

spring boot 和 mybatis已经正常集成,在使用查询时使用的是注解,(项目没有任何XML文件)

1021
来自专栏文渊之博

关于数据存储类型的一点分析

简介     SQL Server每个表中各列的数据类型的有各种形式,产生的效果也各有不同,我们主要根据效率兼顾性能的情况下讨论下如何规定类型。     在SQ...

1896
来自专栏机器人网

中英文对照,瞬间理解西门子PLC指令

指令( 英文全称意思 ) :指令含义 1、LD ( Load 装载 ) :动合触点 2、LDN ( Load Not 不装载 ) : 动断触点 3、A ( A...

3587

扫码关注云+社区

领取腾讯云代金券