SourceFunction中定义了一个void run(SourceContext ctx)方法来启动数据源,SourceContext对象中定义了数据源发送事件数据并生成TimeStamp的方法:
- void collectWithTimeStamp(T element,long timestamp)
第一个参数element代表要发送的元素,第二个参数timestamp代表这个元素对应的时间戳。这个方法只有在设置TimeCharacteristic为EventTime时才有效。当设置为ProcessingTime时,设置的TimeStamp直接忽略。 - void emitWatermark(Watermark mark)
该方法接受一个Watermark类型的参数mark,当发送一个时间戳为T的mark时,表示该数据流上不会再有timestamp小于等于T这个时间的任何事件记录。一般来说,这个mark值T是基于最大的timestamp来生成的,比如以最大timestamp减去1。
代码
package com.atguigu.apitest.example;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.streaming.api.TimeCharacteristic;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.source.SourceFunction;
import org.apache.flink.streaming.api.watermark.Watermark;
import java.util.Arrays;
/**
* @author zhanganjie
* @version 1.0
* @description: TODO
* @date 2021/11/29 12:16
*/
public class Demo01 {
public static void main(String[] args) throws Exception {
//创建流处理环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
//设置时间时间EventTime语义
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
//并行度为1
env.setParallelism(1);
//演示数据
Tuple3[] input = {
Tuple3.of("user1",1000L,1),
Tuple3.of("user1",1999L,2),
Tuple3.of("user1",2000L,3),
Tuple3.of("user1",2100L,4),
Tuple3.of("user1",2130L,5),
};
//通过示例数据生成DataStream
DataStream<Tuple3<String,Long,Integer>> source = env.addSource(
//SourceFunction中进行时间戳分配和水位线生成
new SourceFunction<Tuple3<String, Long, Integer>>() {
@Override
public void run(SourceContext<Tuple3<String, Long, Integer>> sourceContext) throws Exception {
Arrays.asList(input).forEach(tp3 -> {
//指定时间戳
System.out.println("collectWithTimeStamp:"+ (long)tp3.f1);
sourceContext.collectWithTimestamp(tp3, (long) tp3.f1);
//发送水位线
System.out.println("emitWatermark:"+ ((long)tp3.f1-1));
sourceContext.emitWatermark(new Watermark((long) tp3.f1-1));
System.out.println("*******************************************************");
});
//代表结束标志
sourceContext.emitWatermark(new Watermark(Long.MAX_VALUE));
}
@Override
public void cancel() {
}
}
);
//结果打印
source.print();
env.execute();
}
}
对于代码中input对象代表一个数据集,通过addsource可以添加自定义的SourceFunction,在内部的run方法中,通过遍历input数据集,对于每个数据元素,调用sourceContext.collectWithTimstamp(tp3,(long)tp3.f1)来分配时间戳,其中参数tp3代表需要发送的数据,而tp3.f1代表获取数据源中的时间戳字段,并将其类型转换成Llong类型。
对于每个数据元素,再调用source.emitWatermark(new Watermark((long)tp3.f1 - 1))来生成Watermark,其规则为当前元素的时间戳减去1,代表1毫秒的延迟。
生成结果
从上述结果中可以看出,每次接受到一个事件数据,都会调用生成Timestamp和Watermark,其中生成的Watermark值为当前事件的Timestamp减去1。