会话窗口阻止GroupByKey工作

时间:2017-03-29 22:29:29

标签: session google-cloud-dataflow apache-beam

我有一个传入的事件流,每个事件都有一个来自另一个进程的关联sessionId。

我想做的就是使用自定义CombineFn将这些事件组合成一个会话对象。

在开发过程中,我正在使用从文件中读取的有界数据集,以下代码似乎有效:

let json = { 
    a: [1, 2, 3],
    b: [],              // omit b
    c: 1,
    d: "test&encoding", // uriencode
    e: [[4,5],[6,7]],   // flatten this
    f: null,            // omit nulls
    g: 0
};

let qs = to_qs(json)

=> "a=1&a=2&a=3&c=1&d=test%26encoding&e=4&e=5&e=6&e=7&g=0"

上述代码(带输入/输出处理)将输出一系列会话,每个会话中包含多个事件。

input.apply(ParDo.named("ParseEvent").of(new ParseEventFn()))
    .setCoder(KvCoder.of(StringUtf8Coder.of(), AvroCoder.of(Event.class)))
    .apply(GroupByKey.<String, Event>create())
    .apply(Combine.groupedValues(new SessionAccumulator()))

但为了使其能够在无界数据集上工作,我需要应用一个Windowing函数,在本例中是一个SessionWindow。

{sessionId: 1, events: [event1,event2,event3]}
{sessionId: 2, events: [event4,event5]}

在这种情况下,唯一的新代码是Windowing函数,而不是汇总事件,我将每个事件都放在它自己的会话中,如下所示:

input.apply(ParDo.named("ParseEvent").of(new ParseEventFn()))
    .setCoder(KvCoder.of(StringUtf8Coder.of(), AvroCoder.of(Event.class)))
    .apply(Window.<KV<String, Event>>into(Sessions.withGapDuration(Duration.standardMinutes(30))))
    .apply(GroupByKey.<String, Event>create())
    .apply(Combine.groupedValues(new SessionAccumulator()))

知道为什么会这样吗?

编辑:我应该补充一点,ParseEventFn正在使用context.outputWithTimestamp()将时间戳应用于PCollection,并且该时间戳似乎是正确的。

2 个答案:

答案 0 :(得分:2)

在您的情况下,您可以编写自己的WindowFn。如果您将密钥设置为会话ID,那么大间隙持续时间也可以,但它也不能反映数据和计算的性质。

WindowFn的要素是:

  • 您自己的BoundedWindow子类,在这种情况下,您将创建一个包含字段中会话ID的窗口类型
  • assignWindows,您可以在其中将每个元素分配给由会话ID标识的窗口。窗口的长度仍然很重要,因为它控制窗口何时到期并被垃圾收集。
  • mergeWindows,您将合并具有相同会话ID的所有窗口。它们不必落入任何特定的间隙期限内。

您需要注意的另一件事是,控制这些窗口的垃圾收集的水印是由您的无限事件流的来源决定的。因此,在ParDo.of(new ParseEventFn())中设置时间戳对于影响水印来说为时已晚。您可能会丢弃要保留的数据。

答案 1 :(得分:1)

进一步深入研究,在我看来,我的核心假设是时间戳是正确的,这是错误的。

我在窗口之前应用的时间戳是错误的。

Windowing正在做它应该做的事情,但我把时间戳设置得太远了,它为每个事件创建了单独的会话。

糟糕