如何在Flink中管理KeyedBroadcastProcessFunction的键控状态?

时间:2019-01-08 23:23:06

标签: apache-flink flink-streaming

我正在使用private SemaphoreSlim semaphore = new SemaphoreSlim(1); private (CancellationTokenSource cts, Task task)? state; private async Task RestartAsync() { Task task = null; await this.semaphore.WaitAsync(); try { if (this.state.HasValue) { this.state.Value.cts.Cancel(); this.state.Value.cts.Dispose(); try { await this.state.Value.task; } catch (OperationCanceledException) { } this.state = null; } var cts = new CancellationTokenSource(); task = DoSomethingAsync(cts.Token); this.state = (cts, task); } finally { this.semaphore.Release(); } try { await task; } catch (OperationCanceledException) { } } 在Flink中执行流计算。我为我的工作定义了一个扩展BroadcastState的类。假设我有一个以KeyedBroadcastProcessFunction为键的流A,和一个流B,该流广播给所有执行者,以使用我定义的类处理A中的元素。我知道我可以在此类中的(user_id, location)processBroadcastElement中注册一个计时器,以便在超时时可以通过调用processElement来删除特定键组的关联状态。我不知道在那之后,这个关键小组还存在吗?

例如,在流A中,state.clear()附带了一条新消息,并且我们生成了这样的密钥组及其关联状态。之后,如果出现另一条带有(user_id=1, location='usa')的消息,它将触发(user_id=1, location='usa')并发出结果。

说24小时后,我不再对这个密钥组processElement()感兴趣,我可以注册一个计时器来清除关联的状态,但是我无法控制这个密钥组。结果,在24小时之后,当另一个带有(user_id=1, location='usa')的消息出现时,由于此密钥组仍然存在,因此(user_id=1, location='usa')仍将被调用。在作业运行时,尽管它们的关联状态将在24小时后清除,但是密钥组会累积还是不应该与内存使用有关?

相关博客:https://www.da-platform.com/blog/a-practical-guide-to-broadcast-state-in-apache-flink

1 个答案:

答案 0 :(得分:0)

Flink的键控状态组织为分布式(或分片)键值存储,其中键可以是简单的东西,例如整数和字符串,也可以是复合词,例如(user_id = 1,location ='usa')。 密钥组复合密钥不同。密钥组是Flink 1.2中引入的运行时构造(请参见FLINK-3755),以允许有效地重新缩放键值状态。密钥组是密钥空间的子集,并被检查点作为一个独立的单元。在运行时,同一密钥组中的所有密钥在作业图中被分区在一起-每个子任务具有一个或多个完整密钥组的密钥值状态。该design doc提供了更多详细信息。作为使用DataStream API的用户,键组是实现细节,而不是您直接使用的东西。

对于KeyedBroadcastProcessFunction中的计时器,可以在processElementonTimer方法中注册,但不能在processBroadcastElement方法中注册。这是因为计时器始终与某个键相关联,并且没有与广播元素相关联的键。但是,可以通过使用processBroadcastElement对象上的applyToKeyedState方法在KeyedBroadcastProcessFunction.Context方法期间操纵任何或所有键控状态。有关更多详细信息,请参见docs

一旦调用state.clear(),该键的状态条目将被删除。当然,该键的新流事件可能会在状态清除后到达,并且您可以根据需要再次存储该键的值状态。为了避免由于不再使用不再相关的键而保留状态,从而避免了无限制的内存使用,您需要特别小心。您可能希望这样的逻辑在每次创建状态后24小时使状态失效:

processElement:
  if state.value() is null, register timer
  state.update(...)

onTimer:
  state.clear()

或者您可能需要更复杂的逻辑来延长状态的寿命,无论何时更新或访问状态。

另一种选择是使用state time-to-live功能。

更新:

无论何时使用任何ProcessFunction类型的processElementonTimer方法,上下文中都会隐式存在一个特定键,并且对键状态所做的所有操作(例如.update().clear())将仅影响该键的状态。

广播状态的工作方式有所不同。广播状态始终为MapState,并被复制到所有并行子任务中。广播状态是无密钥的-如果您在processElement方法期间读取广播状态,则无论该调用期间上下文中有什么密钥,您都将看到相同的广播状态值。

只有在processBroadcastElement的{​​{1}}方法中,您才能修改(或清除)广播状态,并且重要的是,所有修改(或删除)都必须以相同的方式进行。并行实例。这样设计是为了确保每个并行实例在广播状态下都具有相同的内容。忽略此规则将导致状态不一致,这可能很难调试。有关更多信息,请参见the docs

所以是的,如果您在广播状态下调用.clear(),那么所有键的所有广播状态都将被删除。或者,您可以从广播状态中删除特定项目(请记住,广播状态为MapState),在这种情况下,将针对所有键删除该特定项目。

在Flink培训站点中有几个使用广播状态的示例。见