水善利万物而不争,处众人之所恶,故几于道
在各种APP或购物平台中,支付是非常关键的一步,订单支付实时监控就是针对用户的这个支付过程进行实时的检测和分析,来确保该笔订单能够顺利完成。就我们平时购物支付后返回应用时,他会弹出一个,支付确认中,这其实就是在对接的支付平台实时进行流水号或其他方式的确认。
刨析这个过程,其实有两个流,一个是购物平台的数据流,一个是支付平台的数据流。购物平台产生了支付的数据后,其实这个支付流程并不能算走完,还必须要确认支付平台相应的账户是否到账,否则就算支付失败。所以我们这里用两个日志文件进行订单支付确认的模拟。一个是购物平台的订单日志文件(OrderLog.csv)一个是支付平台的收据日志文件(ReceiptLog.csv)。
来自两个不同平台的日志数据流,要对这两个流中的数据进行交易确认。
可以看到日志数据的格式不一致,
首先我们过滤出订单日志文件中所有的pay事件,因为只有支付了才会有流水号,然后去确认。然后使用connect连接两个流后使用process进行业务处理。具体来说就是比对两个流中是否有相同的交易流水号,如果收据日志文件中有该笔订单支付的交易流水号那么就说明支付平台已经到账了,也就意味着支付成功拉!!!
用来封装购物平台的订单日志数据
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderEvent {
private Long orderId;
private String eventType;
private String txId;
private Long eventTime;
}
用来封装支付平台的收据日志数据
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TxEvent {
private String txId;
private String payChannel;
private Long eventTime;
}
注释很全,就不再赘述了。思路就和分析的一样
public class Flink05_Project_Order {
public static void main(String[] args) {
Configuration conf = new Configuration();
conf.setInteger("rest.port",1000);
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(conf);
env.setParallelism(2);
SingleOutputStreamOperator<OrderEvent> orderEventStream = env
.readTextFile("input/OrderLog.csv")
// 过滤出 pay 事件
.filter(line -> {
String[] datas = line.split(",");
return "pay".equals(datas[1]);
})
// 封装成对象
.map(new MapFunction<String, OrderEvent>() {
@Override
public OrderEvent map(String value) throws Exception {
String[] datas = value.split(",");
return new OrderEvent(
Long.valueOf(datas[0]),
datas[1],
datas[2],
Long.valueOf(datas[3])
);
}
});
SingleOutputStreamOperator<TxEvent> txEventStream = env
.readTextFile("input/ReceiptLog.csv")
// 封装成对象
.map(new MapFunction<String, TxEvent>() {
@Override
public TxEvent map(String value) throws Exception {
String[] datas = value.split(",");
return new TxEvent(
datas[0],
datas[1],
Long.valueOf(datas[2])
);
}
});
// 连接两个不同类型的流
orderEventStream.connect(txEventStream)
// 将连接后的流,用交易流水id分组
.keyBy(new KeySelector<OrderEvent, String>() {
@Override
public String getKey(OrderEvent value) throws Exception {
return value.getTxId();
}
}, new KeySelector<TxEvent, String>() {
@Override
public String getKey(TxEvent value) throws Exception {
return value.getTxId();
}
})
// 用keyed这个能获得的信息多一些,写起来简单
// 泛型的含义分别是key的类型,第一个流输入的类型,第二个流输入的类型,输出类型
.process(new KeyedCoProcessFunction<String, OrderEvent, TxEvent, String>() {
// 存支付订单的支付流水号,这两个流来的先后顺序不一定,如果先来的找不到另一个那就先存起来
// key是流水号,value是事件对象
Map<String,OrderEvent> orderEventMap = new HashMap<>();
// 存支付平台收据日志的流水号
Map<String,TxEvent> txEventMap = new HashMap<>();
@Override
public void processElement1(OrderEvent value, Context ctx, Collector<String> out) throws Exception {
// 获取到订单事件的流水号,然后去收据日志集合里面找有没有这个流水号
TxEvent txEvent = txEventMap.get(value.getTxId());
if (txEvent != null){
// 如果有这个流水号,说明到账了,将相关信息放到流中
out.collect("订单: " + value.getOrderId() + " 对账成功!!!!");
}else{
// 如果没有找到,那就将流水号,和对象放到集合中
orderEventMap.put(ctx.getCurrentKey(),value);
}
}
@Override
public void processElement2(TxEvent value, Context ctx, Collector<String> out) throws Exception {
// 拿到流水号,去订单集合里面找
OrderEvent orderEvent = orderEventMap.get(ctx.getCurrentKey());
if (orderEvent !=null){
// 找到了,将相关信息放到流中
out.collect("订单: " + orderEvent.getOrderId() + " 对账成功!!!!");
}else {
// 没有找到,存起来
txEventMap.put(ctx.getCurrentKey(), value);
}
}
})
// 将流中的信息打印出来
.print();
try {
env.execute();
} catch (Exception e) {
e.printStackTrace();
}
}
}