我们在Google Cloud上的数据流遇到了问题。我们的管道包括各种输入步骤,这些步骤将数据输入到GCP PubSub中。然后,我们汇总数据并对其进行排序。这些1步骤对于Dataflow和我们配置的窗口来说显然太繁琐了。我们在该步骤上得到一个异常[2]。我们还会看到以下指标:
droppedDueToClosedWindow 3,838,662 Bids/AggregateExchangeOrders
droppedDueToClosedWindow 21,060,627 Asks/AggregateExchangeOrders
现在,我正在寻求有关如何解决此问题的建议。我是否应该分解步骤,例如可以使用并行步骤进行迭代和排序?
有没有一种方法可以获取有关到底发生了什么的更多信息? 我们应该增加工人人数吗? (当前为1)。
我们对Dataflow还是比较陌生的。 ..最好的建议是最欢迎的。
编辑:我将在步骤中添加一些细节。
这是将以下步骤“链接”在一起的方式:
@Override
public PCollection<KV<KV<String, String>, List<ExchangeOrder>>> expand(PCollection<KV<String, KV<String, String>>> input) {
return input.apply("PairWithType", new ByPairWithType(type))
.apply("UnfoldExchangeOrders", new ByAggregatedExchangeOrders())
.apply("AggregateExchangeOrders", GroupByKey.<KV<String, String>, KV<String, KV<BigDecimal, BigDecimal>>>create())
.apply("ReorderExchangeOrders", ParDo.of(new ReorderExchangeOrders()));
}
AggregateExchangeOrders:
因此,在这里,很明显,我们遍历了一组订单,并分析了类型(两次),所以它是一个大十进制数。 这让我觉得,我们可以跳过一个解析步骤,如下所示: Convert string to BigDecimal in java
@ProcessElement
public void processElement(ProcessContext c) {
KV<String, KV<String, String>> key = c.element().getKey();
List<KV<String, String>> value = c.element().getValue();
value.forEach(
exchangeOrder -> {
try {
BigDecimal unitPrice = BigDecimal.valueOf(Double.valueOf(exchangeOrder.getKey()));
BigDecimal quantity = BigDecimal.valueOf(Double.valueOf(exchangeOrder.getValue()));
if (quantity.compareTo(BigDecimal.ZERO) != 0) {
// Exclude exchange orders with no quantity.
c.output(KV.of(key.getValue(), KV.of(key.getKey(), KV.of(unitPrice, quantity))));
}
} catch (NumberFormatException e) {
// Exclude exchange orders with invalid element.
}
});
}
...接下来,我们进行分组和排序。 (并且可以选择将其反转),似乎这一步并不会带来很大的负担。
ReorderExchangeOrders:
@ProcessElement
public void processElement(ProcessContext c) {
KV<String, String> pairAndType = c.element().getKey();
Iterable<KV<String, KV<BigDecimal, BigDecimal>>> exchangeOrderBook = c.element().getValue();
List<ExchangeOrder> list = new ArrayList<>();
exchangeOrderBook.forEach(exchangeOrder -> list.add(
new ExchangeOrder(exchangeOrder.getKey(), exchangeOrder.getValue().getKey(), exchangeOrder.getValue().getValue())));
// Asks are sorted in ASC order
Collections.sort(list);
// Bids are sorted in DSC order
if (pairAndType.getValue().equals(EXCHANGE_ORDER_TYPE.BIDS.toString())) {
Collections.reverse(list);
}
c.output(KV.of(pairAndType, list));
}
[1]数据流屏幕截图:
[2]异常:对阶段S8和密钥8的提交请求大于2GB,无法处理。
java.lang.IllegalStateException: Commit request for stage S8 and key 8 is larger than 2GB and cannot be processed. This may be caused by grouping a very large amount of data in a single window without using Combine, or by producing a large amount of data from a single input element.
com.google.cloud.dataflow.worker.StreamingDataflowWorker$Commit.getSize(StreamingDataflowWorker.java:327)
com.google.cloud.dataflow.worker.StreamingDataflowWorker.lambda$new$0(StreamingDataflowWorker.java:342)
答案 0 :(得分:2)
该错误消息很简单。 许多评论指出,此问题的根本原因是,包含一个DoFn的所有结果的结构大于2GB,它们的最佳选择是以某种方式对数据进行分区以使其工作单位较小。
在代码中,我看到DoFn返回的某些结构是KV>形式的嵌套结构。这种安排迫使Dataflow将整个响应发送回一个整体的包中,并防止将其分块为更小的片段。
一种可能的解决方案是在管道中尽可能长地使用复合键而不是嵌套结构,并仅在严格必要时才组合它们。
例如
而不是KV>,DoFn可以返回
KV <(concat(Key1,Key2)),值>
这会将工作单位拆分为更小的集合,然后可以并行地将其分发给多个工人。
要回答其他问题,增加工人数量不会产生任何影响,因为DoFn产生的庞大集合似乎不可分割。添加日志记录以查看集合如何达到2GB可能会提供有用的技巧来防止这种情况。