轻松通关Flink第37讲:自定义 Pattern 和报警规则

在上一课时提过,PatternStream 是 Flink CEP 对模式匹配后流的抽象和定义,它把 DataStream 和 Pattern 组合到一起,并且基于 PatternStream 提供了一系列的方法,比如 select、process 等。

Flink CEP 的核心在于模式匹配,对于不同模式匹配特性的支持,往往决定相应的 CEP 框架是否能够得到广泛应用。那么 Flink CEP 对模式提供了哪些支持呢?

Pattern 分类

Flink CEP 提供了 Pattern API 用于对输入流数据进行复杂事件规则的定义,用来提取符合规则的事件序列。

Flink 中的 Pattern 分为单个模式、组合模式、模式组 3 类。

单个模式

复杂规则中的每一个单独的模式定义,就是个体模式。我们既可以定义一个给定事件出现的次数(量词),也可以定义一个条件来决定一个进来的事件是否被接受进入这个模式(条件)。

例如,我们对一个命名为 start 的模式,可以定义如下量词:

// 期望出现4次 
start.times(4); 
// 期望出现0或者4次 
start.times(4).optional(); 
// 期望出现2、3或者4次 
start.times(2, 4); 
// 期望出现2、3或者4次,并且尽可能地重复次数多 
start.times(2, 4).greedy(); 
// 期望出现0、2、3或者4次 
start.times(2, 4).optional(); 
// 期望出现0、2、3或者4次,并且尽可能地重复次数多 
start.times(2, 4).optional().greedy(); 
// 期望出现1到多次 
start.oneOrMore(); 
// 期望出现1到多次,并且尽可能地重复次数多 
start.oneOrMore().greedy(); 
// 期望出现0到多次 
start.oneOrMore().optional(); 
// 期望出现0到多次,并且尽可能地重复次数多 
start.oneOrMore().optional().greedy(); 
// 期望出现2到多次 
start.timesOrMore(2); 
// 期望出现2到多次,并且尽可能地重复次数多 
start.timesOrMore(2).greedy(); 
// 期望出现0、2或多次 
start.timesOrMore(2).optional(); 
// 期望出现0、2或多次,并且尽可能地重复次数多 
start.timesOrMore(2).optional().greedy();  

我们还可以定义需要的匹配条件:

start.where(new SimpleCondition<Event>() { 
    @Override 
    public boolean filter(Event value) { 
        return value.getName().startsWith("foo"); 
    } 
}); 

上面代码展示了定义一个以“foo”开头的事件。

当然我们还可以把条件组合到一起,例如,可以通过依次调用 where 来组合条件:

pattern.where(new SimpleCondition<Event>() { 
    @Override 
    public boolean filter(Event value) { 
        return ... // 一些判断条件 
    } 
}).or(new SimpleCondition<Event>() { 
    @Override 
    public boolean filter(Event value) { 
        return ... // 一些判断条件 
    } 
}); 
组合模式

我们把很多单个模式组合起来,就形成了组合模式。Flink CEP 支持事件之间如下形式的连续策略:

  • 严格连续,期望所有匹配的事件严格的一个接一个出现,中间没有任何不匹配的事件;

  • 松散连续

  • 不确定的松散连续

我们直接参考官网给出的案例:

// 严格连续 
Pattern<Event, ?> strict = start.next("middle").where(...); 
// 松散连续 
Pattern<Event, ?> relaxed = start.followedBy("middle").where(...); 
// 不确定的松散连续 
Pattern<Event, ?> nonDetermin = start.followedByAny("middle").where(...); 
// 严格连续的NOT模式 
Pattern<Event, ?> strictNot = start.notNext("not").where(...); 
// 松散连续的NOT模式 
Pattern<Event, ?> relaxedNot = start.notFollowedBy("not").where(...); 

对于上述的松散连续不确定的松散连续这里举个例子来说明一下,例如,我们定义了模式“a b”,对于一个事件序列“a,c,b1,b2”,那么会产生下面的结果:

  • “a”和“b”之间严格连续则返回: {} (没有匹配)

  • “a”和“b”之间松散连续则返回: {a b1}

  • “a”和“b”之间不确定的松散连续则返回: {a b1}、{a b2}

模式组

将一个模式作为条件嵌套在单个模式里,就是模式组。我们举例如下:

Pattern<Event, ?> start = Pattern.begin( 
    Pattern.<Event>begin("start").where(...).followedBy("start_middle").where(...) 
); 
// 严格连续 
Pattern<Event, ?> strict = start.next( 
    Pattern.<Event>begin("next_start").where(...).followedBy("next_middle").where(...) 
).times(3); 
// 松散连续 
Pattern<Event, ?> relaxed = start.followedBy( 
    Pattern.<Event>begin("followedby_start").where(...).followedBy("followedby_middle").where(...) 
).oneOrMore(); 
// 不确定松散连续 
Pattern<Event, ?> nonDetermin = start.followedByAny( 
    Pattern.<Event>begin("followedbyany_start").where(...).followedBy("followedbyany_middle").where(...) 
).optional(); 

自定义 Pattern

上一课时定义了几个不同的消息源,我们分别根据需求自定义匹配模式。

  • 第一个场景,连续登录场景

在这个场景中,我们需要找出那些 5 秒钟内连续登录失败的账号,然后禁止用户再次尝试登录需要等待 1 分钟。

Pattern.<LogInEvent>begin("start").where(new IterativeCondition<LogInEvent>() { 
    @Override 
    public boolean filter(LogInEvent value, Context<LogInEvent> ctx) throws Exception { 
        return value.getIsSuccess().equals("fail"); 
    } 
}).next("next").where(new IterativeCondition<LogInEvent>() { 
    @Override 
    public boolean filter(LogInEvent value, Context<LogInEvent> ctx) throws Exception { 
        return value.getIsSuccess().equals("fail"); 
    } 
}).within(Time.seconds(5)); 
  • 第二个场景,超时未支付

在这个场景中,我们需要找出那些下单后 10 分钟内没有支付的订单。

Pattern.<PayEvent> 
        begin("begin") 
        .where(new IterativeCondition<PayEvent>() { 
            @Override 
            public boolean filter(PayEvent payEvent, Context context) throws Exception { 
                return payEvent.getAction().equals("create"); 
            } 
        }) 
        .next("next") 
        .where(new IterativeCondition<PayEvent>() { 
            @Override 
            public boolean filter(PayEvent payEvent, Context context) throws Exception { 
                return payEvent.getAction().equals("pay"); 
            } 
        }) 
        .within(Time.seconds(600)); 
OutputTag<PayEvent> orderTiemoutOutput = new OutputTag<PayEvent>("orderTimeout") {}; 

在这里我们使用了侧输出流,并且将正常的订单流和超时未支付的超时流分开:

SingleOutputStreamOperator selectResult = patternStream.select(orderTiemoutOutput, 
        (PatternTimeoutFunction<PayEvent, ResultPayEvent>) (map, l) -> new ResultPayEvent(map.get("begin").get(0).getUserId(), "timeout"), 
        (PatternSelectFunction<PayEvent, ResultPayEvent>) map -> new ResultPayEvent(map.get("next").get(0).getUserId(), "success") 
); 
DataStream timeOutSideOutputStream = selectResult.getSideOutput(orderTiemoutOutput) 
  • 第三个场景,找出交易活跃用户

在这个场景下,我们需要找出那些 24 小时内至少 5 次有效交易的账户。

Pattern.<TransactionEvent>begin("start").where( 
        new SimpleCondition<TransactionEvent>() { 
            @Override 
            public boolean filter(TransactionEvent transactionEvent) { 
                return transactionEvent.getAmount() > 0; 
            } 
        } 
).timesOrMore(5) 
 .within(Time.hours(24)); 

总结

本一课时我们讲解了 Flink CEP 的模式匹配种类,并且基于上一课时的三个场景自定义了 Pattern,我们在实际生产中可以根据需求定义更为复杂的模式。


精选评论

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
非常感谢您的提问!如果您想要在Flink中获取MySQL多张表的信息,可以按照如下步骤进行: 1. 首先需要在Flink中使用JDBC连接器连接MySQL数据库,并创建一个JDBC输入源,以便从MySQL中读取数据。 2. 然后可以通过Flink的Table API或SQL API将多张表的数据进行连接或者关联,从而得到您需要的数据。 3. 最后可以使用自定义的Sink将数据写入MySQL中。下面就是一个简单的Java代码示例,可以帮助您实现该功能: ``` public class FlinkMySQLSink { public static void main(String[] args) throws Exception { // set up the execution environment StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); // set up JDBC connection options String url = "jdbc:mysql://localhost:3306/test"; String username = "root"; String password = "password"; String driverName = "com.mysql.jdbc.Driver"; // create a JDBC input source to read multiple tables from MySQL JdbcInputFormat jdbcInputFormat = JdbcInputFormat.buildJdbcInputFormat() .setDrivername(driverName) .setDBUrl(url) .setUsername(username) .setPassword(password) .setQuery("SELECT * FROM table1; SELECT * FROM table2;") .finish(); // create a data stream from the JDBC input source DataStream<Tuple2<String, String>> inputDataStream = env.createInput(jdbcInputFormat); // use Table API or SQL API to join or combine multiple tables Table table = inputDataStream .map(new MapFunction<Tuple2<String, String>, Row>() { public Row map(Tuple2<String, String> value) throws Exception { return Row.of(value.f0, value.f1); } }) .toTable(new TableSchema(new String[]{"column1", "column2"}, new TypeInformation[]{Types.STRING, Types.STRING})); // create a custom Sink to write data back to MySQL JDBCOutputFormat jdbcOutputFormat = JDBCOutputFormat.buildJDBCOutputFormat() .setDrivername(driverName) .setDBUrl(url) .setUsername(username) .setPassword(password) .setQuery("INSERT INTO result_table (column1, column2) VALUES (?, ?)") .finish(); // write the data stream to the custom Sink table.writeToSink(jdbcOutputFormat); // execute the Flink job env.execute("Flink MySQL Sink Example"); } } ``` 在这个示例中,我们首先设置了JDBC连接器所需的参数,然后使用JdbcInputFormat创建了一个JDBC输入源,该源可以从MySQL中读取多个表的数据。 接下来,我们使用Table API或SQL API将多个表的数据连接或者关联起来,并生成一个包含所需数据的Table对象。 最后,我们使用自定义的JDBCOutputFormat创建一个Sink,将Table中的数据写回到MySQL中。在这个Sink中,我们需要指定要写入哪个表,以及如何将数据映射到表中的列。 希望这个示例可以帮助您实现获取MySQL多张表信息的功能!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

源码头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值