package com.atguigu.loginfail_detect;
import com.atguigu.loginfail_detect.beans.LoginEvent;
import com.atguigu.loginfail_detect.beans.LoginFailWarning;
import org.apache.flink.api.common.state.ListState;
import org.apache.flink.api.common.state.ListStateDescriptor;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.shaded.guava18.com.google.common.collect.Lists;
import org.apache.flink.streaming.api.TimeCharacteristic;
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.KeyedProcessFunction;
import org.apache.flink.streaming.api.functions.timestamps.BoundedOutOfOrdernessTimestampExtractor;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.util.Collector;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
public class LoginFail {
public static void main(String[] args) throws Exception{
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setParallelism(1);
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
URL resource = LoginFail.class.getResource("/LoginLog.csv");
DataStream<LoginEvent> loginEventStream = env.readTextFile(resource.getPath())
.map(line -> {
String[] fields = line.split(",");
return new LoginEvent(new Long(fields[0]), fields[1], fields[2], new Long(fields[3]));
})
.assignTimestampsAndWatermarks(new BoundedOutOfOrdernessTimestampExtractor<LoginEvent>(Time.seconds(3)) {
@Override
public long extractTimestamp(LoginEvent element) {
return element.getTimestamp() * 1000L;
}
});
SingleOutputStreamOperator<LoginFailWarning> warningStream = loginEventStream
.keyBy(LoginEvent::getUserId)
.process(new LoginFailDetectWarning(2));
warningStream.print();
env.execute("login fail detect job");
}
public static class LoginFailDetectWarning0 extends KeyedProcessFunction<Long, LoginEvent, LoginFailWarning>{
private Integer maxFailTimes;
public LoginFailDetectWarning0(Integer maxFailTimes) {
this.maxFailTimes = maxFailTimes;
}
ListState<LoginEvent> loginFailEventListState;
ValueState<Long> timerTsState;
@Override
public void open(Configuration parameters) throws Exception {
loginFailEventListState = getRuntimeContext().getListState(new ListStateDescriptor<LoginEvent>("login-fail-list", LoginEvent.class));
timerTsState = getRuntimeContext().getState(new ValueStateDescriptor<Long>("timer-ts", Long.class));
}
@Override
public void processElement(LoginEvent value, Context ctx, Collector<LoginFailWarning> out) throws Exception {
if( "fail".equals(value.getLoginState()) ){
loginFailEventListState.add(value);
if( timerTsState.value() == null ){
Long ts = (value.getTimestamp() + 2) * 1000L;
ctx.timerService().registerEventTimeTimer(ts);
timerTsState.update(ts);
}
} else {
if( timerTsState.value() != null )
ctx.timerService().deleteEventTimeTimer(timerTsState.value());
loginFailEventListState.clear();
timerTsState.clear();
}
}
@Override
public void onTimer(long timestamp, OnTimerContext ctx, Collector<LoginFailWarning> out) throws Exception {
ArrayList<LoginEvent> loginFailEvents = Lists.newArrayList(loginFailEventListState.get());
Integer failTimes = loginFailEvents.size();
if( failTimes >= maxFailTimes ){
out.collect( new LoginFailWarning(ctx.getCurrentKey(),
loginFailEvents.get(0).getTimestamp(),
loginFailEvents.get(failTimes - 1).getTimestamp(),
"login fail in 2s for " + failTimes + " times") );
}
loginFailEventListState.clear();
timerTsState.clear();
}
}
public static class LoginFailDetectWarning extends KeyedProcessFunction<Long, LoginEvent, LoginFailWarning> {
private Integer maxFailTimes;
public LoginFailDetectWarning(Integer maxFailTimes) {
this.maxFailTimes = maxFailTimes;
}
ListState<LoginEvent> loginFailEventListState;
@Override
public void open(Configuration parameters) throws Exception {
loginFailEventListState = getRuntimeContext().getListState(new ListStateDescriptor<LoginEvent>("login-fail-list", LoginEvent.class));
}
@Override
public void processElement(LoginEvent value, Context ctx, Collector<LoginFailWarning> out) throws Exception {
if( "fail".equals(value.getLoginState()) ){
Iterator<LoginEvent> iterator = loginFailEventListState.get().iterator();
if( iterator.hasNext() ){
LoginEvent firstFailEvent = iterator.next();
if( value.getTimestamp() - firstFailEvent.getTimestamp() <= 2 ){
out.collect( new LoginFailWarning(value.getUserId(), firstFailEvent.getTimestamp(), value.getTimestamp(), "login fail 2 times in 2s") );
}
loginFailEventListState.clear();
loginFailEventListState.add(value);
} else {
loginFailEventListState.add(value);
}
} else {
loginFailEventListState.clear();
}
}
}
}
5402,83.149.11.115,success,1558430815
23064,66.249.3.15,fail,1558430826
5692,80.149.25.29,fail,1558430833
7233,86.226.15.75,success,1558430832
5692,80.149.25.29,success,1558430840
29607,66.249.73.135,success,1558430841
1035,83.149.9.216,fail,1558430842
1035,83.149.9.216,fail,1558430843
1035,83.149.24.26,fail,1558430844
7328,193.114.45.13,success,1558430848
29607,66.249.73.135,success,1558430847
2133,50.16.19.13,success,1558430857
6745,66.249.73.185,success,1558430859
76456,110.136.166.128,success,1558430853
8345,46.105.14.53,success,1558430855
76456,110.136.166.128,success,1558430857
76456,110.136.166.128,success,1558430854
76456,110.136.166.128,fail,1558430859
76456,110.136.166.128,success,1558430861
3464,123.125.71.35,success,1558430860
76456,110.136.166.128,success,1558430865
65322,50.150.204.184,success,1558430866
23565,207.241.237.225,fail,1558430862
8455,200.49.190.101,success,1558430867
8455,200.49.190.100,success,1558430865
8455,200.49.190.101,success,1558430869
8455,200.49.190.101,success,1558430872
32031,66.249.73.185,success,1558430875
12018,66.249.73.135,success,1558430874
12018,66.249.73.135,success,1558430879
12018,66.249.73.135,success,1558430881
21419,67.214.178.190,success,1558430882
21419,67.214.178.190,success,1558430880
23565,207.241.237.220,success,1558430881
2386,46.105.14.53,success,1558430883
23565,207.241.237.227,success,1558430884
83419,91.177.205.119,success,1558430881
83419,91.177.205.119,fail,1558430882
83419,91.177.205.119,success,1558430885
83419,91.177.205.119,fail,1558430886
83419,91.177.205.119,success,1558430884
83419,91.177.205.119,success,1558430886
4325,26.249.73.15,success,1558430888
2123,207.241.237.228,success,1558430887
21083,207.241.237.101,success,1558430889
13490,87.169.99.232,success,1558430886
93765,209.85.238.199,success,1558430890
93765,209.85.238.199,success,1558430892
输出了,就要清空掉状态,
然后剔除掉最初的那一次登录失败,以第二次的登录失败作为起点