基于集合的Source是将一个普通的Java集合、迭代器或者可变参数转换成一个分布式数据集DataStreamSource,它是DataStream的子类,所以也可以使用DataStream类型来引用。得到DataStream后就可以调用Transformation或Sink度数据进行处理了。
fromCollection(Collection) 方法是一个非并行的Source,可以将一个Collection类型的数据作为参数传入到该方法中,返回一个DataStreamSource。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<Integer> fromCollection = env.fromCollection(Arrays.asList(1, 2, 3, 4, 5, 6));
System.out.println("fromCollection创建的DataStream并行度为:"+fromCollection.getParallelism());
fromCollection.print();
env.execute("");
}
输出结果:
fromCollection创建的DataStream并行度为:1
8> 1
3> 4
1> 2
2> 3
4> 5
5> 6
fromElements(T …) 方法是一个非并行的Source,可以将一到多个数据作为可变参数传入到该方法中,返回DataStreamSource。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<Integer> fromElements = env.fromElements(1, 2, 3, 4, 5, 6);
System.out.println("fromElements创建的DataStream并行度为:"+fromElements.getParallelism());
fromElements.print();
env.execute("");
}
输出结果:
fromElements创建的DataStream并行度为:1
7> 2
6> 1
8> 3
2> 5
1> 4
3> 6
fromParallelCollection(SplittableIterator, Class) 方法是一个并行的Source(并行度可以使用env的setParallelism来设置),该方法需要传入两个参数,第一个是继承SplittableIterator的实现类的迭代器,第二个是迭代器中数据的类型。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<Long> fromParallelCollection = env.fromParallelCollection(new NumberSequenceIterator(1L,6L),Long.class);
System.out.println("fromParallelCollection创建的DataStream并行度为:"+fromParallelCollection.getParallelism());
fromParallelCollection.print();
env.execute("");
}
输出结果:
fromParallelCollection创建的DataStream并行度为:8
5> 5
4> 4
2> 2
6> 6
3> 3
1> 1
generateSequence(long from, long to) 方法是一个并行的Source(并行度也可以通过调用该方法后,再调用setParallelism来设置)该方法需要传入两个long类型的参数,第一个是起始值,第二个是结束值,返回一个DataStreamSource。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<Long> generateSequence = env.generateSequence(1L, 6L);
System.out.println("generateSequence创建的DataStream并行度为:"+generateSequence.getParallelism());
generateSequence.print();
env.execute("");
}
输出结果:
generateSequence创建的DataStream并行度为:8
5> 5
1> 1
6> 6
3> 3
2> 2
4> 4
socketTextStream(String hostname, int port) 方法是一个非并行的Source,该方法需要传入两个参数,第一个是指定的IP地址或主机名,第二个是端口号,即从指定的Socket读取数据创建DataStream。该方法还有多个重载的方法,其中一个是socketTextStream(String hostname, int port, String delimiter, long maxRetry),这个重载的方法可以指定行分隔符和最大重新连接次数。这两个参数,默认行分隔符是”\n”,最大重新连接次数为0。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<String> socketTextStream = env.socketTextStream("localhost",8888);
System.out.println("socketTextStream创建的DataStream并行度为:"+socketTextStream.getParallelism());
socketTextStream.print();
env.execute("");
}
输出结果:
socketTextStream创建的DataStream并行度为:1
5> 1
6> 2
7> 3
8> 4
1> 5
2> 6
提示:如果使用socketTextStream读取数据,在启动Flink程序之前,必须先启动一个Socket服务,为了方便,Mac或Linux用户可以在命令行终端输入nc -lk 8888启动一个Socket服务并在命令行中向该Socket服务发送数据。Windows用户可以在百度中搜索windows安装netcat命令。
基于文件的Source,本质上就是使用指定的FileInputFormat格式读取数据,可以指定TextInputFormat、CsvInputFormat、BinaryInputFormat等格式,基于文件的Source底层都是ContinuousFileMonitoringFunction,这个类继承了RichSourceFunction,它们都是非并行的Source。
readFile(FileInputFormat inputFormat, String filePath) 方法可以指定读取文件的FileInputFormat 格式,其中一个重载的方法readFile(FileInputFormat inputFormat, String filePath, FileProcessingMode watchType, long interval) 可以指定FileProcessingMode,它有两个枚举类型分别是PROCESS_ONCE和PROCESS_CONTINUOUSLY模式,PROCESS_ONCE模式Source只读取文件中的数据一次,读取完成后,程序退出。PROCESS_CONTINUOUSLY模式Source会一直监听指定的文件,如果使用该模式,需要指定检测该文件是否发生变化的时间间隔,但是使用这种模式,文件的内容发生变化后,会将以前的内容和新的内容全部都读取出来,进而造成数据重复读取。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
String path = "src/main/resources/test.txt";
//PROCESS_CONTINUOUSLY模式是一直监听指定的文件或目录,2秒钟检测一次文件是否发生变化
DataStreamSource<String> readFile = env.readFile(new TextInputFormat(null), path,
FileProcessingMode.PROCESS_CONTINUOUSLY, 2000);
System.out.println("readFile创建的DataStream并行度为:"+readFile.getParallelism());
readFile.print();
env.execute("");
}
输入内容:
qwe,71,1
wer,62,2
ert,53,3
rty,44,4
tyu,35,5
yui,26,6 //程序运行后输入的一行
输出结果:
readFile创建的DataStream并行度为:8
7> rty,44,4
4> wer,62,2
8> tyu,35,5
3> qwe,71,1
5> ert,53,3
1> yui,26,6
5> ert,53,3
6> rty,44,4
3> qwe,71,1
7> tyu,35,5
4> wer,62,2
readTextFile(String filePath) 可以从指定的目录或文件读取数据,默认使用的是TextInputFormat格式读取数据,还有一个重载的方法readTextFile(String filePath, String charsetName)可以传入读取文件指定的字符集,默认是UTF-8编码。该方法是一个有限的数据源,数据读完后,程序就会退出,不能一直运行。该方法底层调用的是readFile方法,FileProcessingMode为PROCESS_ONCE。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
String path = "src/main/resources/test.txt";
DataStreamSource<String> readTextFile = env.readTextFile(path);
System.out.println("readTextFile创建的DataStream并行度为:"+readTextFile.getParallelism());
readTextFile.print();
env.execute("");
}
输入内容:
qwe,71,1
wer,62,2
ert,53,3
rty,44,4
tyu,35,5
yui,26,6
输出结果:
readTextFile创建的DataStream并行度为:8
2> wer,62,2
1> qwe,71,1
4> rty,44,4
3> ert,53,3
5> tyu,35,5
7> yui,26,6
首先在maven项目的pom.xml文件中导入Flink跟Kafka整合的依赖:
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka_${scala.binary.version}</artifactId>
<version>${flink.version}</version>
</dependency>
在代码中,创建一个Properties对象,然后设置Kafka的地址和端口、读取偏移量的策略、消费者组ID、并且设置Source读取完Kafka的数据后,定期更新偏移量。然后new FlinkKafkaConsumer,指定三个参数,第一个参数是topic名称;第二个参数是读取文件的反序列化Schema,SimpleStringSchema指的是读取Kafka中的数据反序列化成String格式;第三个参数是传入事先new 好的Properties实例。然后调用env的addSource方法将FlinkKafkaConsumer的实例传入,这样就创建好了一个DataSteamSource。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//设置Kafka相关参数
Properties properties = new Properties();
//设置Kafka的地址和端口
properties.setProperty("bootstrap.servers", "59.XXX.XXX.35:9092,59.XXX.XXX.36:9092,59.XXX.XXX.37:9092");
//读取偏移量策略:如果没有记录偏移量,就从头读,如果记录过偏移量,就接着读
properties.setProperty("auto.offset.reset", "earliest");
//设置消费者组ID
properties.setProperty("group.id", "group1");
//没有开启checkpoint,让flink提交偏移量的消费者定期自动提交偏移量
properties.setProperty("enable.auto.commit", "true");
//创建FlinkKafkaConsumer并传入相关参数
FlinkKafkaConsumer<String> kafkaConsumer = new FlinkKafkaConsumer<>(
"mytest", //要读取数据的Topic名称
new SimpleStringSchema(), //读取文件的反序列化Schema
properties //传入Kafka的参数
);
//使用addSource添加kafkaConsumer
DataStreamSource<String> kafkaStream = env.addSource(kafkaConsumer);
System.out.println("kafkaStream创建的DataStream并行度为:"+kafkaStream.getParallelism());
kafkaStream.print();
env.execute("");
}
输入内容:
{"table":"11ttt","item_id":"111","pkey":["COMPID","ID"],"sub":{"k":"k1"},"subr":{"s":"s1","k":"k1"}}
输出结果:
kafkaStream创建的DataStream并行度为:8
3> {"table":"11ttt","item_id":"111","pkey":["COMPID","ID"],"sub":{"k":"k1"},"subr":{"s":"s1","k":"k1"}}
注意:目前这种方式无法保证Exactly Once,Flink的Source消费完数据后,将偏移量定期的写入到Kafka的一个特殊的topic中,这个topic就是__consumer_offset,这种方式虽然可以记录偏移量,但是无法保证Exactly Once。
Flink的DataStream API可以让开发者根据实际需要,灵活的自定义Source,本质上就是定义一个类,实现SourceFunction这个接口,实现run方法和cancel方法。run方法中实现的就是获取数据的逻辑,然后调用SourceContext的collect方法,将获取的数据收集起来,这样就返回了一个新的DataStreamSource,但是如果只实现这个接口,该Source只能是一个非并行的Source。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<String> mySource = env.addSource(new MySource1());
System.out.println("MySource1创建的DataStream并行度为:"+mySource.getParallelism());
mySource.print();
env.execute("");
}
public static class MySource1 implements SourceFunction<String> {
@Override
public void run(SourceContext<String> sourceContext) throws Exception {
List<String> strs = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
for (String str:strs){
//将Source的数据输出
sourceContext.collect(str);
}
}
@Override
public void cancel() {}
}
输出结果:
MySource1创建的DataStream并行度为:1
8> aaa
4> eee
3> ddd
1> bbb
2> ccc
在生产环境,通常是希望Source可以并行的读取数据,这样读取数据的速度才更快,所以最好的方式是实现ParallelSourceFunction接口或继承RichParallelSourceFunction这个抽象类,同样实现实现run方法和cancel方法,这样该Source就是一个可以并行的Source了。其实所有的Source底层都是调用该的方法。
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
DataStreamSource<String> mySource = env.addSource(new MySource2());
System.out.println("MySource2创建的DataStream并行度为:"+mySource.getParallelism());
mySource.print();
env.execute("");
}
public static class MySource2 implements ParallelSourceFunction<String>{
@Override
public void run(SourceContext<String> sourceContext) throws Exception {
sourceContext.collect("MySource");
}
@Override
public void cancel() {}
}
输出结果;
MySource2创建的DataStream并行度为:8
1> MySource
3> MySource
8> MySource
6> MySource
2> MySource
5> MySource
4> MySource
7> MySource
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
conf.setInteger("rest.port",8082);
StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironmentWithWebUI(conf);
DataStreamSource<String> mySource = env.addSource(new MySource3());
System.out.println("MySource3创建的DataStream并行度为:"+mySource.getParallelism());
mySource.print();
env.execute("");
}
//Rich系列方法执行顺序:
//1.调用MySource的构造方法
//2.调用open方法仅调用一次
//3.调用run方法开始产生数据
//4.调用cancel方法停止run方法
//5.调用close方法释放资源
public static class MySource3 extends RichParallelSourceFunction<String> {
private boolean flag = true;
@Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
System.out.println("SubTask:"+getRuntimeContext().getIndexOfThisSubtask()+" Open MySource!");
}
@Override
public void run(SourceContext<String> sourceContext) throws Exception {
while (flag){
sourceContext.collect(UUID.randomUUID().toString());
Thread.sleep(20000);
}
}
@Override
public void cancel() {
flag = false;
System.out.println("SubTask:"+getRuntimeContext().getIndexOfThisSubtask()+" Cancel MySource!");
}
@Override
public void close() throws Exception {
super.close();
System.out.println("SubTask:"+getRuntimeContext().getIndexOfThisSubtask()+" Close MySource!");
}
}
输出结果:
MySource3创建的DataStream并行度为:8
SubTask:2 Open MySource!
SubTask:4 Open MySource!
SubTask:0 Open MySource!
SubTask:7 Open MySource!
SubTask:1 Open MySource!
SubTask:6 Open MySource!
SubTask:3 Open MySource!
SubTask:5 Open MySource!
5> ea3fd0eb-ba53-44be-b167-2d904c985014
7> 74bb7a0e-8715-424a-aed4-0a4c98ba4e1c
3> d7faf33e-0a38-4c5f-8644-9dd36c46548e
6> 3bb4933b-66a8-4c04-99bf-5e5ecc943d4e
2> 1532c2ba-5904-4af2-869b-27ce5e9a36e6
1> 5a374fc4-0c16-43b5-8e05-ffe6704d47ed
8> 3f3f0375-e0d3-499f-9ac5-754403f12bb5
4> 6884d443-1c02-40e7-878e-dbfa1677a86d
SubTask:0 Cancel MySource!
SubTask:1 Cancel MySource!
SubTask:2 Cancel MySource!
SubTask:4 Cancel MySource!
SubTask:3 Cancel MySource!
SubTask:5 Cancel MySource!
SubTask:6 Cancel MySource!
SubTask:7 Cancel MySource!
SubTask:6 Close MySource!
SubTask:5 Close MySource!
SubTask:3 Close MySource!
SubTask:0 Close MySource!
SubTask:7 Close MySource!
SubTask:4 Close MySource!
SubTask:2 Close MySource!
SubTask:1 Close MySource!