是否有更好的方法来连接更新从两个关键流比时间窗口我正在做的?



所以我有一个Flink作业,它接收来自Kafka的消息,并将创建两个updateObject事件,然后通过作业发送。为简单起见,一个是增加事件,另一个是减少事件(因此消息传入,业务逻辑决定要创建什么updateObjects,以便正确更新状态)。

给出示例updateObject:

{
"batchId":"b1",
"type":"increase",
"amount":1,
"stateToUpdate":"stateAlpha",
"stateToUpdateValue":null
}

updateObjects随后被发送到作业的下游。流由一个值作为键值,updateObject1更新一个状态,updateObject2更新另一个状态。在每个更新它们的状态之后,更新后的值将被放入updateObject的stateToUpdateValue中,并且每个更新后的值将在整个作业中发送。

现在棘手的部分是最后一步。作业的最终输出是来自每个消息的updateObjects数组。可以想到的最好的想法是有一个1秒的滚动时间窗口来收集updateObjects,然后当它在1秒后触发时,它将检查其窗口中的所有内容,并将具有相同batchId的内容配对,并将它们放在最终输出对象中,然后输出它。显然,这并不能保证所有的数据都在1秒内到达那个窗口,但它也会导致处理延迟,因为数据只是停留在那里。

不能保证为每条消息总是创建两个updateObjects,因为它是逐案处理的。由于updateObjects被分割成不同的键流,因为它们的键总是不同的,所以不可能是单个对象通过第一个键状态并更新它,然后使用单个对象对每个键状态进行相应的更新来通过下一个键状态。一旦键控发生,它们就不再连接了。

所以我想知道是否有人能想出更好的方法来做到这一点,因为我觉得肯定有。

您怀疑有更好的方法来做到这一点是对的。

用滚动窗口做这个有两个问题:

  1. 对于通常不会花费那么长时间的事情,您将平均等待半秒,最坏等待一秒钟。
  2. 尽管您经常花费所有时间等待,但基于windows的这种方法仍然很容易出错。即使一个批处理只有两个事件,即使它们在几毫秒内被处理,它们仍然可以落在窗口边界的两端。这是因为一秒钟长的窗口将从12:00:00.000到12:00:00.999,例如,您的事件可能显示在12:00:00.999和12:00:01.001。

这里有一个替代:

在管道的末尾,通过batchId重新键入流,然后使用KeyedProcessFunction将批粘合在一起。

例如,当属于特定批处理的每条记录到达时,将其附加到键控ListState对象。如果您确切地知道每个批处理中有多少条记录,那么您可以在ValueState中保留一个计数器,并且当批处理完成时,您可以遍历列表并生成最终结果(并且在完成时不要忘记清除状态)。或者您可以使用键式计时器等待特定的持续时间(相对于每个批处理的第一条记录的到达),并在计时器触发时生成最终结果。

在Flink的文档中的教程和附带的训练练习中有使用状态函数和过程函数的示例。

或者,您可以使用Flink SQL,使用OVER windows。

最新更新