Flink用于处理有状态的流式计算,需要对Source端的数据进行加工处理,然后写入到Sink端,下图展示了在Flink中数据所经历的过程,今天就根据这张图分别给大家分享下。
UTF-8
8
8
1.12.2
1.8
2.12
com.fasterxml.jackson.core
jackson-databind
2.8.3
org.apache.flink
flink-streaming-java_${scala.binary.version}
${flink.version}
org.apache.flink
flink-clients_2.12
1.12.2
org.apache.flink
flink-connector-kafka_2.11
1.12.2
org.apache.flink
flink-connector-jdbc_2.11
1.12.7
ru.yandex.clickhouse
clickhouse-jdbc
0.3.2
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStream transactions = env
.addSource(new FlinkKafkaConsumer<>(topicName, new SimpleStringSchema(), properties));
transactions.print();
env.execute();
Flink所有的程序都从这一步开始,只有创建了执行环境,才能开始下一步的编写。可以使用如下方式获取运行环境:
创建一个执行环境,表示当前执行程序的上下文
如果程序是独立调用的,则此方法返回本地执行环境
如果从命令行客户端调用程序以提交到集群,则此方法返回此集群的执行环境
会根据查询运行的方式决定返回什么样的运行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
Source即Flink中的数据源,Sink则为数据输出端,Flink通过Flink Streaming Connector来与外部存储系统连接,Flink主要通过四种方式完成数据交换:
Flink预定义的Source与Sink
Flink内部提供的Boundled Connectors
第三方Apache Bahir项目中的连接器
异步IO方式
下面主要对预定义内容及Boundled Connectors作为介绍,更多内容可以参考
先来看一下Flink给我们提供的内置Source,这些方法都位于StreamExecutionEnvironment类中。
Flink中内置的Sink如下图,均位于DataStream类中。
从文本文件中读取数据
env.readTextFile(path)
将结果从文本或 csv 格式写出到文件中
dataStream.writeAsText(path) ;
dataStream.writeAsCsv(path);
在官网中,给出了如下的Connectors:
Apache Kafka (source/sink)
JDBC (sink)
Apache Cassandra (sink)
Amazon Kinesis Streams (source/sink)
Elasticsearch (sink)
FileSystem (sink)
RabbitMQ (source/sink)
Google PubSub (source/sink)
Hybrid Source (source)
Apache NiFi (source/sink)
Apache Pulsar (source)
Twitter Streaming API (source)
在使用过程中,提交 Job 的时候需要注意, job 代码 jar 包中一定要将相应的 connetor 相关类打包进去,否则在提交作业时就会失败,提示找不到相应的类,或初始化某些类异常
除了上述的Source与Sink外,Flink还支持自定义Source与Sink。
实现SourceFunction类
重写run方法和cancel方法
在主函数中通过addSource调用
public class MySource implements SourceFunction {
// 定义一个运行标志位,表示数据源是否运行
Boolean flag = true;
@Override
public void run(SourceContext sourceContext) throws Exception {
while (flag){
sourceContext.collect("当前时间为:" + System.currentTimeMillis());
Thread.sleep(100);
}
}
@Override
public void cancel() {
flag = false;
}
}
继承SinkFunction
重写invoke方法
下面给出了自定义JDBC Sink的案例,可以参考
public class MyJdbcSink extends RichSinkFunction {
// 定义连接
Connection conn;
// 创建连接
@Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
}
// 关闭连接
@Override
public void close() throws Exception {
super.close();
conn.close();
}
// 调用连接执行SQL
@Override
public void invoke(String value, Context context) throws Exception {
PreparedStatement preparedStatement = conn.prepareStatement(value);
preparedStatement.execute();
preparedStatement.close();
}
}
env.addSink(newMyJdbcSink());
rongHeStream.addSink(JdbcSink.sink(
"INSERT INTO ronghe_log VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(preparedStatement, rongHeLog) -> {
preparedStatement.setObject(1, rongHeLog.getId());
preparedStatement.setObject(2, rongHeLog.getDeviceNum());
preparedStatement.setObject(3, rongHeLog.getSrcIp());
preparedStatement.setObject(4, rongHeLog.getSrcPort());
preparedStatement.setObject(5, rongHeLog.getDstIp());
preparedStatement.setObject(6, rongHeLog.getDstPort());
preparedStatement.setObject(7, rongHeLog.getProtocol());
preparedStatement.setObject(8, new Timestamp(rongHeLog.getLastOccurTime()));
//SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//Date date = new Date(rongHeLog.getLastOccurTime());
//String dateStr = simpleDateFormat.format(date);
preparedStatement.setObject(9, rongHeLog.getCount());
try {
String idListJson = objectMapper.writeValueAsString(rongHeLog.getSourceLogIds());
preparedStatement.setObject(10, idListJson);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
},
JdbcExecutionOptions.builder()
.withBatchSize(10)
.build(),
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withDriverName("com.mysql.cj.jdbc.Driver")
.withUrl("jdbc:mysql://81.70.199.213:3306/flink21?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false")
.withUsername("root")
.withPassword("lJPWRbm06NbToDL03Ecj")
.build()
));