前言
前篇文章 《從0到1學習Flink》—— Data Sink 介紹 介紹了 Flink Data Sink,也介紹了 Flink 自帶的 Sink,那麼如何自定義自己的 Sink 呢?這篇文章將寫一個 demo 教大家將從 Kafka Source 的資料 Sink 到 MySQL 中去。
準備工作
我們先來看下 Flink 從 Kafka topic 中獲取資料的 demo,首先你需要安裝好了 FLink 和 Kafka 。
執行啟動 Flink、Zookepeer、Kafka,
好了,都啟動了!
資料庫建表
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(25) COLLATE utf8_bin DEFAULT NULL,
`password` varchar(25) COLLATE utf8_bin DEFAULT NULL,
`age` int(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;複製程式碼
實體類
Student.java
package com.zhisheng.flink.model;
/**
* Desc:
* weixin: zhisheng_tian
* blog: http://www.54tianzhisheng.cn/
*/
public class Student {
public int id;
public String name;
public String password;
public int age;
public Student() {
}
public Student(int id, String name, String password, int age) {
this.id = id;
this.name = name;
this.password = password;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}複製程式碼
工具類
工具類往 kafka topic student 傳送資料
import com.alibaba.fastjson.JSON;
import com.zhisheng.flink.model.Metric;
import com.zhisheng.flink.model.Student;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerRecord;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* 往kafka中寫資料
* 可以使用這個main函式進行測試一下
* weixin: zhisheng_tian
* blog: http://www.54tianzhisheng.cn/
*/
public class KafkaUtils2 {
public static final String broker_list = "localhost:9092";
public static final String topic = "student"; //kafka topic 需要和 flink 程式用同一個 topic
public static void writeToKafka() throws InterruptedException {
Properties props = new Properties();
props.put("bootstrap.servers", broker_list);
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
KafkaProducer producer = new KafkaProducer<String, String>(props);
for (int i = 1; i <= 100; i++) {
Student student = new Student(i, "zhisheng" + i, "password" + i, 18 + i);
ProducerRecord record = new ProducerRecord<String, String>(topic, null, null, JSON.toJSONString(student));
producer.send(record);
System.out.println("傳送資料: " + JSON.toJSONString(student));
}
producer.flush();
}
public static void main(String[] args) throws InterruptedException {
writeToKafka();
}
}複製程式碼
SinkToMySQL
該類就是 Sink Function,繼承了 RichSinkFunction ,然後重寫了裡面的方法。在 invoke 方法中將資料插入到 MySQL 中。
package com.zhisheng.flink.sink;
import com.zhisheng.flink.model.Student;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
/**
* Desc:
* weixin: zhisheng_tian
* blog: http://www.54tianzhisheng.cn/
*/
public class SinkToMySQL extends RichSinkFunction<Student> {
PreparedStatement ps;
private Connection connection;
/**
* open() 方法中建立連線,這樣不用每次 invoke 的時候都要建立連線和釋放連線
*
* @param parameters
* @throws Exception
*/
@Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
connection = getConnection();
String sql = "insert into Student(id, name, password, age) values(?, ?, ?, ?);";
ps = this.connection.prepareStatement(sql);
}
@Override
public void close() throws Exception {
super.close();
//關閉連線和釋放資源
if (connection != null) {
connection.close();
}
if (ps != null) {
ps.close();
}
}
/**
* 每條資料的插入都要呼叫一次 invoke() 方法
*
* @param value
* @param context
* @throws Exception
*/
@Override
public void invoke(Student value, Context context) throws Exception {
//組裝資料,執行插入操作
ps.setInt(1, value.getId());
ps.setString(2, value.getName());
ps.setString(3, value.getPassword());
ps.setInt(4, value.getAge());
ps.executeUpdate();
}
private static Connection getConnection() {
Connection con = null;
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8", "root", "root123456");
} catch (Exception e) {
System.out.println("-----------mysql get connection has exception , msg = "+ e.getMessage());
}
return con;
}
}複製程式碼
Flink 程式
這裡的 source 是從 kafka 讀取資料的,然後 Flink 從 Kafka 讀取到資料(JSON)後用阿里 fastjson 來解析成 student 物件,然後在 addSink 中使用我們建立的 SinkToMySQL,這樣就可以把資料儲存到 MySQL 了。
package com.zhisheng.flink;
import com.alibaba.fastjson.JSON;
import com.zhisheng.flink.model.Student;
import com.zhisheng.flink.sink.SinkToMySQL;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.sink.PrintSinkFunction;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer011;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer011;
import java.util.Properties;
/**
* Desc:
* weixin: zhisheng_tian
* blog: http://www.54tianzhisheng.cn/
*/
public class Main3 {
public static void main(String[] args) throws Exception {
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("zookeeper.connect", "localhost:2181");
props.put("group.id", "metric-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("auto.offset.reset", "latest");
SingleOutputStreamOperator<Student> student = env.addSource(new FlinkKafkaConsumer011<>(
"student", //這個 kafka topic 需要和上面的工具類的 topic 一致
new SimpleStringSchema(),
props)).setParallelism(1)
.map(string -> JSON.parseObject(string, Student.class)); //Fastjson 解析字串成 student 物件
student.addSink(new SinkToMySQL()); //資料 sink 到 mysql
env.execute("Flink add sink");
}
}複製程式碼
結果
執行 Flink 程式,然後再執行 KafkaUtils2.java 工具類,這樣就可以了。
如果資料插入成功了,那麼我們檢視下我們的資料庫:
資料庫中已經插入了 100 條我們從 Kafka 傳送的資料了。證明我們的 SinkToMySQL 起作用了。是不是很簡單?
專案結構
怕大家不知道我的專案結構,這裡發個截圖看下:
最後
本文主要利用一個 demo,告訴大家如何自定義 Sink Function,將從 Kafka 的資料 Sink 到 MySQL 中,如果你專案中有其他的資料來源,你也可以換成對應的 Source,也有可能你的 Sink 是到其他的地方或者其他不同的方式,那麼依舊是這個套路:繼承 RichSinkFunction 抽象類,重寫 invoke 方法。
關注我
轉載請務必註明原創地址為:www.54tianzhisheng.cn/2018/10/31/…
微信公眾號:zhisheng
另外我自己整理了些 Flink 的學習資料,目前已經全部放到微信公眾號(zhisheng)了,你可以回覆關鍵字:Flink 即可無條件獲取到。另外也可以加我微信 你可以加我的微信:yuanblog_tzs,探討技術!
更多私密資料請加入知識星球!
Github 程式碼倉庫
以後這個專案的所有程式碼都將放在這個倉庫裡,包含了自己學習 flink 的一些 demo 和部落格
部落格
1、Flink 從0到1學習 —— Apache Flink 介紹
2、Flink 從0到1學習 —— Mac 上搭建 Flink 1.6.0 環境並構建執行簡單程式入門
3、Flink 從0到1學習 —— Flink 配置檔案詳解
4、Flink 從0到1學習 —— Data Source 介紹
5、Flink 從0到1學習 —— 如何自定義 Data Source ?
6、Flink 從0到1學習 —— Data Sink 介紹
7、Flink 從0到1學習 —— 如何自定義 Data Sink ?
8、Flink 從0到1學習 —— Flink Data transformation(轉換)
9、Flink 從0到1學習 —— 介紹 Flink 中的 Stream Windows
10、Flink 從0到1學習 —— Flink 中的幾種 Time 詳解
11、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 ElasticSearch
12、Flink 從0到1學習 —— Flink 專案如何執行?
13、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 Kafka
14、Flink 從0到1學習 —— Flink JobManager 高可用性配置
15、Flink 從0到1學習 —— Flink parallelism 和 Slot 介紹
16、Flink 從0到1學習 —— Flink 讀取 Kafka 資料批量寫入到 MySQL
17、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 RabbitMQ
18、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 HBase
19、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 HDFS
20、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 Redis
21、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 Cassandra
22、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 Flume
23、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 InfluxDB
24、Flink 從0到1學習 —— Flink 讀取 Kafka 資料寫入到 RocketMQ
25、Flink 從0到1學習 —— 你上傳的 jar 包藏到哪裡去了
26、Flink 從0到1學習 —— 你的 Flink job 日誌跑到哪裡去了
28、Flink 從0到1學習 —— Flink 中如何管理配置?
29、Flink 從0到1學習—— Flink 不可以連續 Split(分流)?
30、Flink 從0到1學習—— 分享四本 Flink 國外的書和二十多篇 Paper 論文
32、為什麼說流處理即未來?
33、OPPO 資料中臺之基石:基於 Flink SQL 構建實時資料倉儲
36、Apache Flink 結合 Kafka 構建端到端的 Exactly-Once 處理
38、如何基於Flink+TensorFlow打造實時智慧異常檢測平臺?只看這一篇就夠了
40、Flink 全網最全資源(視訊、部落格、PPT、入門、實戰、原始碼解析、問答等持續更新)
42、Flink 從0到1學習 —— 如何使用 Side Output 來分流?
原始碼解析
4、Flink 原始碼解析 —— standalone session 模式啟動流程
5、Flink 原始碼解析 —— Standalone Session Cluster 啟動流程深度分析之 Job Manager 啟動
6、Flink 原始碼解析 —— Standalone Session Cluster 啟動流程深度分析之 Task Manager 啟動
7、Flink 原始碼解析 —— 分析 Batch WordCount 程式的執行過程
8、Flink 原始碼解析 —— 分析 Streaming WordCount 程式的執行過程
9、Flink 原始碼解析 —— 如何獲取 JobGraph?
10、Flink 原始碼解析 —— 如何獲取 StreamGraph?
11、Flink 原始碼解析 —— Flink JobManager 有什麼作用?
12、Flink 原始碼解析 —— Flink TaskManager 有什麼作用?
13、Flink 原始碼解析 —— JobManager 處理 SubmitJob 的過程
14、Flink 原始碼解析 —— TaskManager 處理 SubmitJob 的過程
15、Flink 原始碼解析 —— 深度解析 Flink Checkpoint 機制
16、Flink 原始碼解析 —— 深度解析 Flink 序列化機制
17、Flink 原始碼解析 —— 深度解析 Flink 是如何管理好記憶體的?
18、Flink Metrics 原始碼解析 —— Flink-metrics-core
19、Flink Metrics 原始碼解析 —— Flink-metrics-datadog
20、Flink Metrics 原始碼解析 —— Flink-metrics-dropwizard
21、Flink Metrics 原始碼解析 —— Flink-metrics-graphite
22、Flink Metrics 原始碼解析 —— Flink-metrics-influxdb
23、Flink Metrics 原始碼解析 —— Flink-metrics-jmx
24、Flink Metrics 原始碼解析 —— Flink-metrics-slf4j
25、Flink Metrics 原始碼解析 —— Flink-metrics-statsd
26、Flink Metrics 原始碼解析 —— Flink-metrics-prometheus
27、Flink 原始碼解析 —— 如何獲取 ExecutionGraph ?
30、Flink Clients 原始碼解析原文出處:zhisheng的部落格,歡迎關注我的公眾號:zhisheng