Java Apache beam GroupByKey重复事件

Java Apache beam GroupByKey重复事件,java,google-cloud-dataflow,apache-beam,google-cloud-pubsub,Java,Google Cloud Dataflow,Apache Beam,Google Cloud Pubsub,我有一个java中的apache beam管道,如下所示: pipeline .apply("Read pubsub",PubsubIO.readStrings() .fromTopic(inputTopic) ) .apply(window) .apply(ParDo.of(new OrderCodeKey())) .apply(GroupByKey.<Stri

我有一个java中的apache beam管道,如下所示:

        pipeline
        .apply("Read pubsub",PubsubIO.readStrings()
                .fromTopic(inputTopic)
        )
        .apply(window)
        .apply(ParDo.of(new OrderCodeKey()))
        .apply(GroupByKey.<String, RawBsonDocument>create())
        .apply(ParDo.of(new GetLastOrder()))
        .apply(ParDo.of(new ShopIDKey()))
        .apply(GroupByKey.create())
        .apply(ParDo.of(new CountShopOrder()))
        ;
管道
.apply(“读取pubsub”,PubsubIO.readStrings()
.fromTopic(输入选项)
)
.应用(窗口)
.apply(ParDo.of(new OrderCodeKey()))
.apply(GroupByKey.create())
.apply(ParDo.of(new GetLastOrder()))
.apply(ParDo.of(new ShopIDKey()))
.apply(GroupByKey.create())
.apply(新CountShopOrder()的第页)
;
问题描述:我需要从pubsub主题流式处理订单事件,每个事件的类型为“更新”或“插入”,并且对应于mongodb中订单的更新/插入操作。在每个事件中都有一些值得注意的领域:

  • clusterTime->操作发生的时间
  • 订单代码->订单代码
  • 店铺标识->订单所属的店铺
我的目标是计算每家商店的总订单。

管道详情:

  • 从pubsub主题阅读
  • 创建键值对(用于后续GroupByKey)键是订单代码,值是文档
公共静态类OrderCodeKey扩展了DoFn{
@过程元素
public void processElement(@Element String order,OutputReceiver out)引发ParseException异常{
//解析JSON
RawBsonDocument document=RawBsonDocument.parse(顺序);
BsonDocument fullDocument=document.getDocument(“fullDocument”);
String orderCode=fullDocument.getString(“订单代码”).getValue();
//整数orderCode=0;
输出(千伏(订单代码、文件));
}
}
  • GroupByKey(按订单分组\u代码)
  • 将具有相同订单代码的事件合并以获取最新订单(clusterTime最大的订单)
公共静态类GetLastOrder扩展了DoFn{
@过程元素
public void processElement(@Element KV orders,OutputReceiver out)抛出ParseException,java.text.ParseException{
List docs=Lists.newArrayList(orders.getValue());
Collections.sort(文档、新比较器(){
@凌驾
公共整数比较(RAWSONDOCUMENT o1,RAWSONDOCUMENT o2){
BsonTimestamp t1=o1.get(“clusterTime”).asTimestamp();
BsonTimestamp t2=o2.get(“clusterTime”).asTimestamp();
返回t2.compareTo(t1);//sort desc
}
});
RawBsonDocument document=docs.iterator().next();
输出(千伏(orders.getKey(),document.getDocument(“fullDocument”));
}
  • 为后续GroupByKey创建新密钥(key=shop\u id)
公共静态类ShopIDKey扩展了DoFn{
@过程元素
public void processElement(@Element KV order,OutputReceiver out)引发异常{
Long shopId=Long.valueOf(order.getValue().getInt32(“shop_id”).getValue());
输出(千伏of(shopId,order.getValue());
}
}
  • GroupByKey(按店铺id分组)
  • 统计每个商店的订单数量
公共静态类CountShopOrder扩展DoFn{
@过程元素
public void processElement(@Element KV orders,OutputReceiver out)抛出ParseException,java.text.ParseException{
List docs=Lists.newArrayList(orders.getValue());
Long countOrder=Long.valueOf(docs.size());
}
}
在最后一步中,我假设输入的Iterable orders将只包含唯一的orders(因为在GroupByKey GetLastOrder之后,只保留最新的事件).但是,当我调试时,我收到了具有相同顺序代码的事件,我已经通过GetLastOrder ParDo减少了该顺序代码。下面是我使用的窗口和触发器:

Window<String> window = Window.<String>into(
                FixedWindows.of(Duration.standardMinutes(60))
                )
                .triggering(
                        AfterWatermark.pastEndOfWindow()
                        .withEarlyFirings(AfterProcessingTime.pastFirstElementInPane())
                )
                .withAllowedLateness(Duration.standardSeconds(0))
                .accumulatingFiredPanes();
Window-Window=Window.into(
固定窗口(持续时间标准分钟(60))
)
.触发(
AfterWatermark.pastEndOfWindow()
.WithEarlyFiregs(在处理Time.pastFirstElementInPane()之后)
)
.允许延迟(持续时间.标准秒(0))
.积聚燃烧的汽油();

如果您不需要中间结果,只需要每小时汇总每家店铺的唯一订单,则无需提前触发

当您使用
.WithEarlyFireings(AfterProcessingTime.pastFirstElementInPane())
时,在第一个数据到达窗格后,每个窗格将仅处理数据的子集。With
.AccumatingFiredPanes()
,您将看到上一个窗格的累积结果,因此相同的数据在同一窗口的不同窗格中多次出现


有关详细信息,请参阅。

如果您不需要中间结果,而只需要每小时汇总每家店铺的唯一订单,则无需提前触发

当您使用
.WithEarlyFireings(AfterProcessingTime.pastFirstElementInPane())
时,在第一个数据到达窗格后,每个窗格将仅处理数据的子集。With
.AccumatingFiredPanes()
,您将看到上一个窗格的累积结果,因此相同的数据在同一窗口的不同窗格中多次出现


有关详细信息,请参阅。

你好,ihji,谢谢你的回答是的,我不需要中间结果,但我还希望每次出现新订单时都重新计算结果(新事件推送到pubsub)。我了解
。AccumatingFiredPanes()
制作最后一个阶段(CountShopOrder)要接收具有相同顺序代码的多个事件,是否仍然只获取窗格的最新结果?嗨,ihji,谢谢你的回答是的,我不需要中间结果
public static class GetLastOrder extends DoFn<KV<String, Iterable<RawBsonDocument>>, KV<String, BsonDocument>>{
        @ProcessElement
        public void processElement (@Element KV<String, Iterable<RawBsonDocument>> orders, OutputReceiver<KV<String, BsonDocument>> out) throws ParseException, java.text.ParseException {
            List<RawBsonDocument> docs = Lists.newArrayList(orders.getValue());
            Collections.sort(docs, new Comparator<RawBsonDocument>(){
                @Override
                public int compare(RawBsonDocument o1, RawBsonDocument o2) {
                    BsonTimestamp t1 = o1.get("clusterTime").asTimestamp();
                    BsonTimestamp t2 = o2.get("clusterTime").asTimestamp();
                    return t2.compareTo(t1); // sort desc
                }
            });
            RawBsonDocument document = docs.iterator().next();
            out.output(KV.of(orders.getKey(), document.getDocument("fullDocument")));
        }
public static class ShopIDKey extends DoFn<KV<String, BsonDocument>, KV<Long, BsonDocument>>{
        @ProcessElement
        public void processElement (@Element KV<String, BsonDocument> order, OutputReceiver<KV<Long, BsonDocument>> out) throws ParseException {
        Long shopId =  Long.valueOf(order.getValue().getInt32("shop_id").getValue());
        out.output(KV.of(shopId, order.getValue()));
        }
    }
public static class CountShopOrder extends DoFn<KV<Long, Iterable<BsonDocument>>, Shop>{
        @ProcessElement
        public void processElement (@Element KV<Long, Iterable<BsonDocument>> orders, OutputReceiver<Shop> out) throws ParseException, java.text.ParseException {
            List<BsonDocument> docs = Lists.newArrayList(orders.getValue());
            Long countOrder = Long.valueOf(docs.size());
        }
    }
Window<String> window = Window.<String>into(
                FixedWindows.of(Duration.standardMinutes(60))
                )
                .triggering(
                        AfterWatermark.pastEndOfWindow()
                        .withEarlyFirings(AfterProcessingTime.pastFirstElementInPane())
                )
                .withAllowedLateness(Duration.standardSeconds(0))
                .accumulatingFiredPanes();