Flink CEP 超时预警实现案例

该博客通过实例展示了如何使用Flink CEP实现离厂超时预警,详细介绍了定义刷卡事件类、构建事件模式、获取超时输出流的步骤,并提供了订单超时统计、空气质量检测等其他CEP应用案例。

0.GitHub项目连接:

Flink CEP 人员离场超时预警案例

1. 案例-离厂超时预警 实现思路

利用Flink CEP 的实现思路,暂时没考虑其他的干扰条件。假定只刷卡 出 进一次。

/**
解决思路:
	利用Flink CEP进行时间流的模式匹配,并设定超时时间(90 minutes)。
具体如下(简单思路,只考虑员工 出 进一次,即door_status从2变为1):
	按照某个员工的id聚合的某日的刷卡进出事件:
    员工入厂,进,door_status=1
    员工中途离厂,出,door_status=2
    员工再次进厂,进,door_status=1
    员工中途离厂,出,door_status=2,如果距离上次刷卡出厂的时间超过90minutes仍未检测到员工刷卡入厂,则进行超时预警。
*/

/**
 *  从员工刷卡出门开始,与当前时间做比较,相差90分钟以上时,则检测此员工的刷卡进入状态,如果没有检测到,则进行离厂预警。
 *  *****
 *  1.CEP 复杂事件处理
 *  *****
 *  1.1	定义一个Pattern,匹配出Pattern里in door与out door间隔大于90分钟的事件
 *  1.2	对这些进行输出预警,获取超时未匹配的流
 *  *****

1.1 定义一个刷卡事件类
  • 1.IN: DataSource -> DataStream -> Transformations -> DataStream -> keyBy ->KeyedStream
  • 2.Pattern:Pattern.begin.where.next.where…within(Time windowTime)
  • 3.PatternStream:CEP.pattern(KeyedStream,Pattern)
  • 4.OutputTag:new OutputTag(…)
  • 5.SingleOutputStreamOperator: PatternStream.flatSelect(OutputTag,PatternFlatTimeoutFunction,PatternFlatSelectFunction)
  • 6.DataStream:SingleOutputStreamOperator.getSideOutput(OutputTag)
  • 7.OUT:DataStream -> Transformations -> DataStream -> DataSink
//AccessEvent,刷卡访问事件
package com.events;

import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.Objects;

/**
 *  AccessEvent 刷卡访问事件的实体类对象
 * */
//@Data
//@AllArgsConstructor
@NoArgsConstructor
public class AccessEvent implements Serializable {
   
   
    public Integer id;
    public Integer door_id;
    public String door_status;
    public Integer event_type;
    public String employee_sys_no;
    public String datetime;

    public AccessEvent(AccessEvent indoor) {
   
   
    }


    public int getId() {
   
   
        return id;
    }

    public void setId(int id) {
   
   
        this.id = id;
    }

    public int getDoor_id() {
   
   
        return door_id;
    }

    public void setDoor_id(int door_id) {
   
   
        this.door_id = door_id;
    }

    public String getDoor_status() {
   
   
        return door_status;
    }

    public void setDoor_status(String door_status) {
   
   
        this.door_status = door_status;
    }

    public int getEvent_type() {
   
   
        return event_type;
    }

    public void setEvent_type(int event_type) {
   
   
        this.event_type = event_type;
    }

    public String getEmployee_sys_no() {
   
   
        return employee_sys_no;
    }

    public void setEmployee_sys_no(String employee_sys_no) {
   
   
        this.employee_sys_no = employee_sys_no;
    }

    public String getDatetime() {
   
   
        return datetime;
    }

    public void setDatetime(String datetime) {
   
   
        this.datetime = datetime;
    }

    public AccessEvent(int id, int door_id, String door_status, int event_type, String employee_sys_no, String datetime) {
   
   
        this.id = id;
        this.door_id = door_id;
        this.door_status = door_status;
        this.event_type = event_type;
        this.employee_sys_no = employee_sys_no;
        this.datetime = datetime;
    }

    @Override
    public boolean equals(Object o) {
   
   
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AccessEvent that = (AccessEvent) o;
        return id == that.id &&
                door_id == that.door_id &&
                door_status == that.door_status &&
                event_type == that.event_type &&
                employee_sys_no == that.employee_sys_no &&
                Objects.equals(datetime, that.datetime);
    }

    @Override
    public int hashCode() {
   
   
        return Objects.hash(id, door_id, door_status, event_type, employee_sys_no, datetime);
    }
    @Override
    public String toString() {
   
   
        return "AccessEvent{" +
                "id=" + id +
                ", door_id=" + door_id +
                ", door_status=" + door_status +
                ", event_type=" + event_type +
                ", employee_sys_no=" + employee_sys_no +
                ", datetime='" + datetime + '\'' +
                '}';
    }
}
1.2 定义一个事件模式(Pattern)
        /**
         * 定义一个事件模式(Pattern)
         * */
        Pattern<AccessEvent,AccessEvent> warningPattern=Pattern.<AccessEvent>begin("outdoor")
                .where(new SimpleCondition<AccessEvent>() {
   
   
                    private static final long serialVersionUID = -6847788055093903603L;

                    @Override
                    public boolean filter(AccessEvent accessEvent) throws Exception {
   
   
                        return accessEvent.getDoor_status().equals("2");
                    }
                })
                .next("indoor").where(new SimpleCondition<AccessEvent>() {
   
   
                    @Override
                    public boolean filter(AccessEvent accessEvent) throws Exception {
   
   
                        return accessEvent.getDoor_status().equals("1");
                    }
                })
                .within(Time.seconds(10)).times(1);//为方便测试,这里将间隔时间设置为10s
/**
可以设置Pattern模式的属性,固定次数(times)、匹配发生一次以上(oneOrMore)、匹配发生多次以上(timesOrMore)           
*/
1.3 Build pattern stream,模式匹配输出
PatternStream<AccessEvent> accessEventPatternStream=CEP.pattern(dataStreamKeyBy,warningPattern);;//按照员工ID去匹配
1.4 Use side output get timeout stream,获取超时输出流
/**
	创建OutputTag利用side output 获取超时未匹配的流
*/
        OutputTag<AccessEvent> outputTag=new OutputTag<AccessEvent>("timedout"){
   
   
            private static final long serialVersionUID = 773503794597666247L;
        };
        SingleOutputStreamOperator<AccessEvent> timeout=accessEventPatternStream.flatSelect(
                outputTag,
                new AccessTimedOut(),
                new FlatSelect()
        );
    /**
     * 把超时的事件收集起来
     * */
    public static class AccessTimedOut implements PatternFlatTimeoutFunction<AccessEvent,AccessEvent> {
   
   
        private static final long serialVersionUID = -4214641891396057732L;
        @Override
        public void timeout(Map<String, List<AccessEvent>> pattern, long timeStamp, Collector<AccessEvent> out) throws Exception {
   
   
            if (null!=pattern.get("outdoor")){
   
   
                for (AccessEvent accessEvent:pattern.get("outdoor")){
   
   
                    System.out.println("timeout outdoor:"+accessEvent.getEmployee_sys_no());
                    out.collect(accessEvent);
                }
            }
            //因为indoor 超时了,还没有收到indoor,所以这里是拿不到 indoor 的
            System.out.println("timeout end"+pattern.get("indoor"));
        }
    }

案例 Demo

package com;
import com.events.AccessEvent;
import com.utils.JsonFilter;
import com.utils.KafkaConfigUtil;
import org.apache.flink.api.common.functions.FilterFunction;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.restartstrategy.RestartStrategies;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.cep.*;
import org.apache.flink.cep.pattern.Pattern;
import org.apache.flink.cep.pattern.conditions.SimpleCondition;
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.CheckpointConfig;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.AssignerWithPeriodicWatermarks;
import org.apache.flink.streaming.api.functions.IngestionTimeExtractor;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
import org.apache.flink.util.Collector;
import org.apache.flink.util.OutputTag;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.Tuple6;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class Test2 {
   
   
    private static Logger log = LoggerFactory.getLogger(Test2.class);

    public static void main(String[] args) throws Exception {
   
   
        
如今的大数据技术应用场景,对实时性的要求已经越来越高。作为新一代大数据流处理框架,由于非常好的实时性,Flink独树一帜,在近些年引起了业内极大的兴趣和关注。Flink能够提供毫秒级别的延迟,同时保证了数据处理的低延迟、高吞吐和结果的正确性,还提供了丰富的时间类型和窗口计算、Exactly-once 语义支持,另外还可以进行状态管理,并提供了CEP(复杂事件处理)的支持。Flink在实时分析领域的优势,使得越来越多的公司开始将实时项目向Flink迁移,其社区也在快速发展壮大。目前,Flink已经成为各大公司实时领域的发力重点,特别是国内以阿里为代表的一众大厂,都在全力投入,不少公司为Flink社区贡献了大量源码。如今Flink已被很多人认为是大数据实时处理的方向和未来,很多公司也都在招聘和储备了解掌握Flink的人才。本教程将Flink理论与电商数据分析项目实战并重,对Flink基础理论知识做了系统的梳理和阐述,并通过电商用户行为分析的具体项目用多个指标进行了实战演练。为有志于增加大数据项目经验、扩展流式处理框架知识的工程师提供了学习方式。二、教程内容和目标本教程主要分为两部分:第一部分,主要是Flink基础理论的讲解,涉及到各种重要概念、原理和API的用法,并且会有大量的示例代码实现;第二部分,以电商作为业务应用场景,以Flink作为分析框架,介绍一个电商用户行为分析项目的开发实战。通过理论和实际的紧密结合,可以使学员对Flink有充分的认识和理解,在项目实战中对Flink和流式处理应用的场景、以及电商分析业务领域有更深刻的认识;并且通过对流处理原理的学习和与批处理架构的对比,可以对大数据处理架构有更全面的了解,为日后成长为架构师打下基础。三、谁适合学1、有一定的 Java、Scala 基础,希望了解新的大数据方向的编程人员2、有 Java、Scala 开发经验,了解大数据相关知识,希望增加项目经验的开发人员3、有较好的大数据基础,希望掌握Flink及流式处理框架的求职人员
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值