在电商平台中,最终创造收入和利润的是用户下单购买的环节;更具体一点,是用户真正完成支付动作的时候。用户下单的行为可以表明用户对商品的需求,但在现实中,并不是每次下单都会被用户立刻支付。当拖延一段时间后,用户支付的意愿会降低。所以为了让用户更有紧迫感从而提高支付转化率,同时也为了防范订单支付环节的安全风险,电商网站往往会对订单状态进行监控,设置一个失效时间(比如15分钟),如果下单后一段时间仍未支付,订单就会被取消。
本文将使用FlinkCEP库来实现这个功能。
1.用户下单之后,应设置订单失效时间,以提高用户支付的意愿,并降低系统风险
2.用户下单后15分钟未支付,则输出监控信息
利用 CEP 库进行事件流的模式匹配,并设定匹配的时间间隔。
我们简化数据为:用户ID(orderId Long),事件类型(eventType String),事件时间(eventTime Long)。
其中时间类型包括:create 和 pay
我们先将事件流按照订单号orderId分流,然后定义这样的一个事件模式:在15分钟内,事件“create”与“pay”非严格连续
可以叫做OrderTimeoutDetect。
因为需要使用CEP库 所以要引入相关依赖
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-cep_2.11</artifactId>
<version>1.9.1</version>
</dependency>
其他还有Flink通用的依赖
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java_2.11</artifactId>
<version>1.9.1</version>
</dependency>
编译打包方式官方推荐用maven-shade-plugin。完整pom链接
定义OrderEvent 完整类输入的订单事件流;另外定义OrderResult 完整类为输出显示的订单状态结果。
先创建流作业的环境已经简单的配置 使用的是事件时间
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
env.setParallelism(1);
测试用的自定义数据
DataStream<OrderEvent> dataSource = env.fromCollection(Arrays.asList(
new OrderEvent(1L, "create", 1558430842L),
new OrderEvent(2L, "create", 1558430843L),
new OrderEvent(2L, "pay", 1558430844L)
))
有了数据源之后,我们需要对数据流按照orderId做keyby分流
KeyedStream<OrderEvent, Long> orderEventStream = dataSource.assignTimzestampsAndWatermarks(new AscendingTimestampExtractor<OrderEvent>() {
@Override
public long extractAscendingTimestamp(OrderEvent element) {
return element.getEventTime() * 1000;
}
})
.keyBy(new KeySelector<OrderEvent, Long>() {
@Override
public Long getKey(OrderEvent value) throws Exception {
return value.getOrderId();
}
});
在做keyBy之前日常加上waterMark。因为我们这里的时间的顺序的 所以用assignTimzestampsAndWatermarks就行,生产中很少用。
这样就得到了一个KeyedStream,留着备用。
Pattern<OrderEvent, OrderEvent> OrderPayPattern = Pattern.<OrderEvent>begin("begin").where(new SimpleCondition<OrderEvent>() {
@Override
public boolean filter(OrderEvent value) throws Exception {
return value.getEventType().equals("create");
}
})
//非严格连续
.followedBy("follow").where(new SimpleCondition<OrderEvent>() {
@Override
public boolean filter(OrderEvent value) throws Exception {
return value.getEventType().equals("pay");
}
})
//设定超时时间
.within(Time.seconds(15));
OutputTag<OrderResult> orderTimeoutOutput = new OutputTag<OrderResult>("orderTimeout"){};
特别注意:不是简单的new 一个类,是一个匿名内部类
PatternStream<OrderEvent> patternStream = CEP.pattern(orderEventStream, OrderPayPattern);
DataStream<OrderResult> completedDataStream = patternStream.select(
//第一个参数 刚定义的超时输出的标签
orderTimeoutOutput,
//第二个参数 处理超时的流
new PatternTimeoutFunction<OrderEvent, OrderResult>() {
@Override
public OrderResult timeout(Map<String, List<OrderEvent>> pattern, long timeoutTimestamp) throws Exception {
Long timeoutOrderId = pattern.getOrDefault("begin", null).iterator().next().getOrderId();
return new OrderResult(timeoutOrderId, "timeout");
}
},
//第三个参数 处理正常的流
new PatternSelectFunction<OrderEvent, OrderResult>() {
@Override
public OrderResult select(Map<String, List<OrderEvent>> pattern) throws Exception {
Long payOrderId = pattern.getOrDefault("follow", null).iterator().next().getOrderId();
return new OrderResult(payOrderId, "success");
}
});
//正常支付
completedDataStream.print();
//超时支付
DataStream<OrderResult> sideOutput = ((SingleOutputStreamOperator<OrderResult>) completedDataStream).getSideOutput(orderTimeoutOutput);sideOutput.print();
env.execute("order timeout job");
完整的代码链接
https://github.com/comsir/Flink-UserBehaviorAnalysis/tree/master/OrderTimeoutDetect