【问题标题】:Apache Flink - stateful function for keeping calculation contextApache Flink - 用于保持计算上下文的有状态函数
【发布时间】:2021-12-22 21:37:57
【问题描述】:

当我对部分数据执行一些计算时,我有一个用例,这些计算取决于上下文(中间状态)。

例如:我有一些订单并对其进行一些计算。对按交易品种字段分组的订单执行计算。

    class Order {
        LocalDateTime ts;
        String symbol;
        ....
    }

所以我决定按符号字段键入订单并为每个组保留单独的状态:

    DataStream<Order> orders = tableEnv.toDataStream(selectStatement, Order.class);
    orders.keyBy(Order::getSymbol).flatMap(new SymbolExecutionContext()).addSink(jdbcSink);

带状态的函数:

 public class SymbolExecutionContext extends RichFlatMapFunction<Order, OrderBookRow> {

    private transient ValueState<OrderBook> orderBookState;

    @Override
    public void flatMap(Order input, Collector<OrderBookRow> out) throws Exception {
        OrderBook orderBook = this.orderBookState.value();
        if (orderBook == null) {
            orderBook = new OrderBook(input.getSymbol());
            this.orderBookState.update(orderBook);
            orderBook = this.orderBookState.value();
        }
        final List<OrderBookRow> execute = OrderBookService.execute(input, orderBook);
        for (final OrderBookRow orderBookRow : execute) {
            out.collect(orderBookRow);
        }
        this.orderBookState.update(orderBook);
    }

    @Override
    public void open(Configuration config) {
        ValueStateDescriptor<OrderBook> descriptor = new ValueStateDescriptor<OrderBook>("orderbook", OrderBook.class, null);
        orderBookState = getRuntimeContext().getState(descriptor);
    }
}

所以我为每个独特的符号创建了新的 OrderBook。 OrderBook 表示符号上下文,其中对具有相同符号的每个订单执行一些计算。

但它似乎不起作用。如果只有一个符号,它会很好地工作。如果不止一个,它会产生无效的结果(缺失或不准确),并且结果或多或少是不可预测的。

Flink 作业以批处理方式执行。

有没有更好的方法来处理这个用例?

【问题讨论】:

  • 还有什么重要的,我希望以有序的方式处理订单(按时间戳字段排序)

标签: apache-flink flink-batch


【解决方案1】:

您似乎仅在状态不存在时才更新订单。这是一个有效的假设吗?进来的订单不会影响订单簿吗?

【讨论】:

  • 传入订单可能会修改 OrderBook(状态),它在 OrderBookService.execute(input, orderBook) 中进行。 OrderBook 是对 ValueState&lt;OrderBook&gt; orderBookState 中保存的对象的引用,因此我不必在每次出现新订单时都更新状态。没有价值的时候只做一次。即使每次有新订单进入时我运行orderBook.update(orderBook),它的行为也完全相同。我更新了SymbolExecutionContext,这样你就可以看到我所做的更改
【解决方案2】:

DataStream API 用于批量执行模式时,事件先按key排序,再按时间戳排序,这样应该可以满足您的排序要求。

由于键是顺序符号,因此将处理第一个符号的所有事件(按时间排序),然后是下一个符号,依此类推。有趣的是,您说这仅在只有一个符号时才能正常工作。这是什么版本的 Flink?您如何设置表和流执行环境,以及如何配置批处理模式?

你不应该想到

orderBook = this.orderBookState.value();

作为返回对您可以更新的对象的引用。 (对于某些 Flink 的状态后端来说,这可能是偶然的,但它不是公共接口的一部分。)相反,每次你想更新状态时都应该调用 orderBookState.update(orderBook)

【讨论】:

  • OrderBookService.execute() 是否需要OrderBook 记录的列表,或者只是最近的记录?如果是列表,则需要使用ListState,而不是ValueState
  • 我也想知道,但 OrderBook 可能是(或包含)一个列表。 ListState 在 RocksDB 上的性能将比 ValueState&lt;List&gt; 好得多,但对于批处理我相信这并不重要。
猜你喜欢
  • 1970-01-01
  • 2020-07-27
  • 2020-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-14
相关资源
最近更新 更多