1.异步IO
在Flink 流处理过程中,经常需要和外部系统进行交互,如通过维度表补全事实表中的维度字段。
默认情况下,在Flink 算子中,单个并行子任务只能以同步方式与外部系统交互:将请求发送到外部存储,IO阻塞,等待请求返回,然后继续发送下一个请求。这种方式将大量时间耗费在了等待结果上。
为了提高处理效率,可以有两种思路。
(1)增加算子的并行度,但需要耗费更多的资源。
(2)异步 IO。
Flink 在1.2中引入了Async I/O,将IO操作异步化。在异步模式下,单个并行子任务可以连续发送多个请求,按照返回的先后顺序对请求进行处理,发送请求后不需要阻塞式等待,省去了大量的等待时间,大幅提高了流处理效率。
Async I/O 是阿里巴巴贡献给社区的特性,呼声很高,可用于解决与外部系统交互时网络延迟成为系统瓶颈的问题。
异步查询实际上是把维表的查询操作托管给单独的线程池完成,这样不会因为某一个查询造成阻塞,因此单个并行子任务可以连续发送多个请求,从而提高并发效率。对于涉及网络IO的操作,可以显著减少因为请求等待带来的性能损耗。
以下为flink异步IO代码片段
//4.1 关联SKU
SingleOutputStreamOperator<TradeTrademarkCategoryUserSpuOrderBean> withSkuDS = AsyncDataStream.unorderedWait(
skuUserOrderDS,
new DimAsyncFunction<TradeTrademarkCategoryUserSpuOrderBean>("DIM_SKU_INFO") {
@Override
public String getKey(TradeTrademarkCategoryUserSpuOrderBean input) {
return input.getSkuId();
}
@Override
public void join(TradeTrademarkCategoryUserSpuOrderBean input, JSONObject dimInfo) {
if (dimInfo != null) {
System.out.println("CATEGORY3_ID:" + dimInfo.getString("CATEGORY3_ID"));
input.setSpuId(dimInfo.getString("SPU_ID"));
input.setTrademarkId(dimInfo.getString("TM_ID"));
input.setCategory3Id(dimInfo.getString("CATEGORY3_ID"));
}
}
},
60, TimeUnit.SECONDS);
//4.2 关联SPU
SingleOutputStreamOperator<TradeTrademarkCategoryUserSpuOrderBean> withSpuDS = AsyncDataStream.unorderedWait(
withSkuDS,
new DimAsyncFunction<TradeTrademarkCategoryUserSpuOrderBean>("DIM_SPU_INFO") {
@Override
public String getKey(TradeTrademarkCategoryUserSpuOrderBean input) {
return input.getSpuId();
}
@Override
public void join(TradeTrademarkCategoryUserSpuOrderBean input, JSONObject dimInfo) {
if (dimInfo != null) {
input.setSpuName(dimInfo.getString("SPU_NAME"));
}
}
},
60, TimeUnit.SECONDS);
模板类 DimAsyncFunction,在其中定义了维度关联的具体流程
- 根据流中对象获取维度主键。
- 根据维度主键获取维度对象。
- 用上一步的查询结果补全流中对象的维度信息。
public abstract class DimAsyncFunction<T> extends RichAsyncFunction<T, T> implements DimJoinFunction<T> {
private Connection connection;
private ThreadPoolExecutor threadPoolExecutor;
private String tableName;
public DimAsyncFunction(String tableName) {
this.tableName = tableName;
}
@Override
public void open(Configuration parameters) throws Exception {
connection = DriverManager.getConnection(GmallConfig.PHOENIX_SERVER);
threadPoolExecutor = ThreadPoolUtil.getThreadPoolExecutor();
}
@Override
public void asyncInvoke(T input, ResultFuture<T> resultFuture) throws Exception {
threadPoolExecutor.execute(new Runnable() {
@SneakyThrows
@Override
public void run() {
//1.查询维表数据
JSONObject dimInfo = DimUtil.getDimInfo(connection, tableName, getKey(input));
//2.将维表数据补充到JavaBean中
if (dimInfo != null) {
join(input, dimInfo);
}
//3.将补充之后的数据输出
resultFuture.complete(Collections.singletonList(input));
}
});
}
@Override
public void timeout(T input, ResultFuture<T> resultFuture) throws Exception {
//再次查询补充信息
System.out.println("TimeOut:" + input);
}
}