需求说明:在这个案例中,我们需要找出那些 5 秒钟内连续登录失败的账号,然后禁止用户,再次尝试登录需要等待 1 分钟。
LoginEvent:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author wanglin
* @description TODO
* @date 2022-06-09 7:46
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class LoginEvent {
private Long userId;
private String isSuccess;
private Long timeStamp;
}
AlertEvent:
package com.flink.cep.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author wanglin
* @description TODO
* @date 2022-06-09 7:52
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AlertEvent {
private String id;
private String message;
}
/**
* @BelongsProject: FlinkCEP
* @BelongsPackage: com.flink.cep.bean
* @Author: wanglin
* @CreateTime: 2022-06-09 07:52
* @Description: TODO
* @Version: 1.0
*/
LoginEvent主类:
package com.flink.cep.code;
import com.flink.cep.bean.AlertEvent;
import com.flink.cep.bean.LoginEvent;
import edu.umd.cs.findbugs.annotations.Nullable;
import org.apache.flink.api.java.functions.KeySelector;
import org.apache.flink.cep.CEP;
import org.apache.flink.cep.PatternStream;
import org.apache.flink.cep.functions.PatternProcessFunction;
import org.apache.flink.cep.pattern.Pattern;
import org.apache.flink.cep.pattern.conditions.IterativeCondition;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks;
import org.apache.flink.streaming.api.watermark.Watermark;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.util.Collector;
import java.util.List;
import java.util.Map;
public class LoginEventDemo {
public static void main(String[] args) throws Exception {
// 需求:在这个案例中,我们需要找出那些 5 秒钟内连续登录失败的账号,然后禁止用户,再次尝试登录需要等待 1 分钟。
// 创建执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
// 构建数据源,提取事件时间戳并生成watermark 并根据UserId分组
DataStream<LoginEvent> source = env.fromElements(
new LoginEvent(1L, "fail", 1597905234000L),
new LoginEvent(1L, "success", 1597905235000L),
new LoginEvent(2L, "fail", 1597905236000L),
new LoginEvent(2L, "fail", 1597905237000L),
new LoginEvent(2L, "fail", 1597905238000L),
new LoginEvent(3L, "fail", 1597905239000L),
new LoginEvent(3L, "success", 1597905240000L)
).assignTimestampsAndWatermarks(new BoundedOutOfOrdernessGenerator()).keyBy(new KeySelector<LoginEvent, Object>() {
@Override
public Object getKey(LoginEvent value) throws Exception {
return value.getUserId();
}
});
// 定义模式匹配规则
Pattern<LoginEvent, LoginEvent> pattern = Pattern.<LoginEvent>begin("start").where(new IterativeCondition<LoginEvent>() {
@Override
public boolean filter(LoginEvent value, Context<LoginEvent> context) throws Exception {
return value.getIsSuccess().equals("fail");
}
}).next("next").where(new IterativeCondition<LoginEvent>() {
@Override
public boolean filter(LoginEvent value, Context<LoginEvent> context) throws Exception {
return value.getIsSuccess().equals("fail");
}
}).timesOrMore(2)
.within(Time.seconds(5));
// 5秒内连续登录多次
/*Pattern<LoginEvent, LoginEvent> patternStream = Pattern.<LoginEvent>begin("start").where(new IterativeCondition<LoginEvent>() {
@Override
public boolean filter(LoginEvent value, Context<LoginEvent> context) throws Exception {
return value.getIsSuccess().equals("fail");
}
}).timesOrMore(2)
.within(Time.seconds(5));*/
// 将模式作用在流上
PatternStream<LoginEvent> patternStream = CEP.pattern(source, pattern);
// 检测模式
SingleOutputStreamOperator<AlertEvent> process = patternStream.process(new PatternProcessFunction<LoginEvent, AlertEvent>() {
@Override
public void processMatch(Map<String, List<LoginEvent>> match, Context ctx, Collector<AlertEvent> out) throws Exception {
List<LoginEvent> start = match.get("start");
List<LoginEvent> next = match.get("next");
System.out.println("start:" + start + ",next:" + next);
}
});
process.printToErr();
// 启动任务
env.execute("LoginEventDemo");
}
private static class BoundedOutOfOrdernessGenerator implements AssignerWithPeriodicWatermarks<LoginEvent> {
private final long maxOutOfOrderness = 5000L;
private long currentTimeStamp;
@Nullable
@Override
public Watermark getCurrentWatermark() {
return new Watermark(currentTimeStamp - maxOutOfOrderness);
}
@Override
public long extractTimestamp(LoginEvent element, long previousElementTimestamp) {
Long timeStamp = element.getTimeStamp();
currentTimeStamp = Math.max(timeStamp, currentTimeStamp);
System.err.println(element.toString() + ",EventTime:" + timeStamp + ",watermark:" + (currentTimeStamp - maxOutOfOrderness));
return timeStamp;
}
}
}
测试结果:
LoginEvent(userId=1, isSuccess=fail, timeStamp=1597905234000),EventTime:1597905234000,watermark:1597905229000
LoginEvent(userId=1, isSuccess=success, timeStamp=1597905235000),EventTime:1597905235000,watermark:1597905230000
LoginEvent(userId=2, isSuccess=fail, timeStamp=1597905236000),EventTime:1597905236000,watermark:1597905231000
LoginEvent(userId=2, isSuccess=fail, timeStamp=1597905237000),EventTime:1597905237000,watermark:1597905232000
LoginEvent(userId=2, isSuccess=fail, timeStamp=1597905238000),EventTime:1597905238000,watermark:1597905233000
LoginEvent(userId=3, isSuccess=fail, timeStamp=1597905239000),EventTime:1597905239000,watermark:1597905234000
LoginEvent(userId=3, isSuccess=success, timeStamp=1597905240000),EventTime:1597905240000,watermark:1597905235000
start:[LoginEvent(userId=2, isSuccess=fail, timeStamp=1597905236000)],next:[LoginEvent(userId=2, isSuccess=fail, timeStamp=1597905237000), LoginEvent(userId=2, isSuccess=fail, timeStamp=1597905238000)]