Apache flink 为什么CEP没有';使用ProcessingTime时,是否仅在输入第二个事件后才打印第一个事件?
我向卡夫卡发送了一个isStart为true的事件,并让Flink使用卡夫卡中的事件,还将TimeCharacteristic设置为ProcessingTime并设置为(Time.seconds(5)),因此我希望CEP在我发送第一个事件的5秒后打印事件,但它没有,在我把第二件事发给卡夫卡之后,它才打印出第一件事。为什么它只在我发送了两个事件之后才打印第一个事件?当我使用ProcessingTime发送第一个事件5秒后,是否应该打印事件 代码如下:Apache flink 为什么CEP没有';使用ProcessingTime时,是否仅在输入第二个事件后才打印第一个事件?,apache-flink,flink-cep,Apache Flink,Flink Cep,我向卡夫卡发送了一个isStart为true的事件,并让Flink使用卡夫卡中的事件,还将TimeCharacteristic设置为ProcessingTime并设置为(Time.seconds(5)),因此我希望CEP在我发送第一个事件的5秒后打印事件,但它没有,在我把第二件事发给卡夫卡之后,它才打印出第一件事。为什么它只在我发送了两个事件之后才打印第一个事件?当我使用ProcessingTime发送第一个事件5秒后,是否应该打印事件 代码如下: public class LongRidesW
public class LongRidesWithKafka {
private static final String LOCAL_ZOOKEEPER_HOST = "localhost:2181";
private static final String LOCAL_KAFKA_BROKER = "localhost:9092";
private static final String RIDE_SPEED_GROUP = "rideSpeedGroup";
private static final int MAX_EVENT_DELAY = 60; // rides are at most 60 sec out-of-order.
public static void main(String[] args) throws Exception {
final int popThreshold = 1; // threshold for popular places
// set up streaming execution environment
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime);
Properties kafkaProps = new Properties();
//kafkaProps.setProperty("zookeeper.connect", LOCAL_ZOOKEEPER_HOST);
kafkaProps.setProperty("bootstrap.servers", LOCAL_KAFKA_BROKER);
kafkaProps.setProperty("group.id", RIDE_SPEED_GROUP);
// always read the Kafka topic from the start
kafkaProps.setProperty("auto.offset.reset", "earliest");
// create a Kafka consumer
FlinkKafkaConsumer011<TaxiRide> consumer = new FlinkKafkaConsumer011<>(
"flinktest",
new TaxiRideSchema(),
kafkaProps);
// assign a timestamp extractor to the consumer
//consumer.assignTimestampsAndWatermarks(new CustomWatermarkExtractor());
DataStream<TaxiRide> rides = env.addSource(consumer);
DataStream<TaxiRide> keyedRides = rides.keyBy("rideId");
// A complete taxi ride has a START event followed by an END event
Pattern<TaxiRide, TaxiRide> completedRides =
Pattern.<TaxiRide>begin("start")
.where(new SimpleCondition<TaxiRide>() {
@Override
public boolean filter(TaxiRide ride) throws Exception {
return ride.isStart;
}
})
.next("end")
.where(new SimpleCondition<TaxiRide>() {
@Override
public boolean filter(TaxiRide ride) throws Exception {
return !ride.isStart;
}
});
// We want to find rides that have NOT been completed within 120 minutes
PatternStream<TaxiRide> patternStream = CEP.pattern(keyedRides, completedRides.within(Time.seconds(5)));
OutputTag<TaxiRide> timedout = new OutputTag<TaxiRide>("timedout") {
};
SingleOutputStreamOperator<TaxiRide> longRides = patternStream.flatSelect(
timedout,
new LongRides.TaxiRideTimedOut<TaxiRide>(),
new LongRides.FlatSelectNothing<TaxiRide>()
);
longRides.getSideOutput(timedout).print();
env.execute("Long Taxi Rides");
}
public static class TaxiRideTimedOut<TaxiRide> implements PatternFlatTimeoutFunction<TaxiRide, TaxiRide> {
@Override
public void timeout(Map<String, List<TaxiRide>> map, long l, Collector<TaxiRide> collector) throws Exception {
TaxiRide rideStarted = map.get("start").get(0);
collector.collect(rideStarted);
}
}
public static class FlatSelectNothing<T> implements PatternFlatSelectFunction<T, T> {
@Override
public void flatSelect(Map<String, List<T>> pattern, Collector<T> collector) {
}
}
private static class TaxiRideTSExtractor extends AscendingTimestampExtractor<TaxiRide> {
private static final long serialVersionUID = 1L;
@Override
public long extractAscendingTimestamp(TaxiRide ride) {
// Watermark Watermark = getCurrentWatermark();
if (ride.isStart) {
return ride.startTime.getMillis();
} else {
return ride.endTime.getMillis();
}
}
}
private static class CustomWatermarkExtractor implements AssignerWithPeriodicWatermarks<TaxiRide> {
private static final long serialVersionUID = -742759155861320823L;
private long currentTimestamp = Long.MIN_VALUE;
@Override
public long extractTimestamp(TaxiRide ride, long previousElementTimestamp) {
// the inputs are assumed to be of format (message,timestamp)
if (ride.isStart) {
this.currentTimestamp = ride.startTime.getMillis();
return ride.startTime.getMillis();
} else {
this.currentTimestamp = ride.endTime.getMillis();
return ride.endTime.getMillis();
}
}
@Nullable
@Override
public Watermark getCurrentWatermark() {
return new Watermark(currentTimestamp == Long.MIN_VALUE ? Long.MIN_VALUE : currentTimestamp - 1);
}
}
卡夫卡公共课{
私有静态最终字符串LOCAL\u ZOOKEEPER\u HOST=“localhost:2181”;
私有静态最终字符串LOCAL_KAFKA_BROKER=“localhost:9092”;
专用静态最终字符串行驶速度组=“行驶速度组”;
私有静态final int MAX_EVENT_DELAY=60;//游戏最多有60秒不正常。
公共静态void main(字符串[]args)引发异常{
final int-popThreshold=1;//热门位置的阈值
//设置流执行环境
StreamExecutionEnvironment env=StreamExecutionEnvironment.getExecutionEnvironment();
环境setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime);
属性Kafkapprops=新属性();
//Kafkapprops.setProperty(“zookeeper.connect”,本地\ zookeeper \主机);
setProperty(“bootstrap.servers”,本地卡夫卡代理);
Kafkapprops.setProperty(“group.id”,行驶速度组);
//始终从一开始就阅读卡夫卡主题
Kafkapprops.setProperty(“auto.offset.reset”、“最早”);
//创建卡夫卡消费者
FlinkKafkaConsumer011消费者=新的FlinkKafkaConsumer011(
“燧石测试”,
新出租车路线图(),
卡夫卡普);
//将时间戳提取器分配给使用者
//consumer.assignTimestampsAndWatermarks(新的CustomWatermarkExtractor());
DataStream rides=env.addSource(消费者);
DataStream keyedRides=rides.keyBy(“rideId”);
//一次完整的滑行过程有一个开始事件和一个结束事件
模式完成骑乘=
模式。开始(“开始”)
.where(新的SimpleCondition(){
@凌驾
公共布尔筛选器(出租车行驶)引发异常{
返回ride.isStart;
}
})
.下一步(“结束”)
.where(新的SimpleCondition(){
@凌驾
公共布尔筛选器(出租车行驶)引发异常{
return!ride.isStart;
}
});
//我们想找到120分钟内还没有完成的游乐设施
PatternStream PatternStream=CEP.pattern(keyedRides,completedRides.within(Time.seconds(5));
OutputTag timedout=新的OutputTag(“timedout”){
};
SingleOutputStreamOperator longRides=patternStream.flatSelect(
蒂梅杜特,
新的长途汽车。TaxiRideTimedOut(),
新的LongRides.FlatSelectNothing()
);
longRides.getSideOutput(timedout.print();
环境执行(“长途出租车”);
}
公共静态类TaxirideTimeOut实现PatternFlatTimeOut函数{
@凌驾
公共无效超时(映射、长l、收集器)引发异常{
滑行设施启动=map.get(“开始”).get(0);
收集器。收集器(已启动);
}
}
公共静态类FlatSelectNothing实现PatternFlatSelectFunction{
@凌驾
公共void flatSelect(地图样式、收集器){
}
}
专用静态类TaxiRideTSExtractor扩展AscendingTimestampExtractor{
私有静态最终长serialVersionUID=1L;
@凌驾
公共长取上升时间戳(出租车乘坐){
//水印水印=getCurrentWatermark();
如果(ride.isStart){
返回骑乘。startTime.getMillis();
}否则{
return ride.endTime.getMillis();
}
}
}
私有静态类CustomWatermarkExtractor使用PeriodicWatermarks实现AssignerWithPeriodicWatermarks{
私有静态最终长serialVersionUID=-742759155861320823L;
私有长currentTimestamp=long.MIN_值;
@凌驾
公共长提取时间戳(出租车行驶,长前元素时间戳){
//假设输入为格式(消息、时间戳)
如果(ride.isStart){
this.currentTimestamp=ride.startTime.getMillis();
返回骑乘。startTime.getMillis();
}否则{
this.currentTimestamp=ride.endTime.getMillis();
return ride.endTime.getMillis();
}
}
@可空
@凌驾
公共水印getCurrentWatermark(){
返回新水印(currentTimestamp==Long.MIN_值?Long.MIN_值:currentTimestamp-1);
}
}
}原因是Flink的CEP库目前仅在另一个元素到达并被处理时检查时间戳。基本假设是你有一个稳定的事件流 我认为这是Flink的CEP库的一个局限性。为了正确工作,Flink应该将处理时间计时器注册为
arrivalTime+timeout
,如果没有事件到达,就会触发模式超时