我正在将微服务作为事件源集合实现,而后者又实现为Flink FlatMapFunction。在基本设置中,聚合从两个kafka主题中读取事件和命令。然后,它将新事件写入第一个主题,并在第三个主题中处理结果。因此,卡夫卡充当活动商店。希望这张图有所帮助:
RPC Request RPC Result
| |
~~~~> Commands-| |---> Results ~~~~~~|
|-->Aggregate--|
~> Input evs. -| |---> output evs. ~~~
| |
~~~~~<~~~~~~~~~~~<~~~feedbak loop~~~~~<~~~~~~~~<~~~
由于Kafka没有检查,因此命令可能会被重播两次,并且输出事件似乎也可以写成主题的两倍。
在重复消息的情况下,如何恢复状态?聚合是否有可能知道其输入流何时是最新的以开始处理命令?
我想过几个解决方案:
如果Flink实现了回滚未确认事件,则可以实现一个Sink,它将从事件源获取当前偏移量。重新启动时,此接收器将删除kafka主题中的new-than-offset事件。按照他的方式,KafkaSource和KafkaSink将从同一个构建器生成,然后暴露给拓扑。该解决方案存在一个很大的问题,因为其他服务可以读取主题中的较新事件并导致不一致。
如果在2中无法从Flink中删除事件,则statefull源可能会从偏移量中读取事件并尝试匹配聚合中的重复事件并删除它们。这种选择看起来并不健全,因为可能存在这样的情况:补丁不是确定性的并且存在缺陷,因为它应该针对每个聚合和拓扑进行重新考虑,并且它不能保证恢复(例如,在连续重启的情况下)。因此,这是一个糟糕的解决方案。
这是一种不同的方法。它是创建一个带有两个特殊水印的特殊KafkaSource:第一个是KafkaSourceStartedWatermark,它将始终在源启动时发送,以通知相关的运营商。发送此水印时,源会在内部记录当前的Kafka偏移量。第二个,KafkaSourceUpToDateWatermark,在达到偏移量时由源发送。这些水印将沿着拓扑透明地传播。操作员应该能够处理这些Watermarks,实现一个特殊的WatermarkNotifiable接口。然后,聚合将能够缓冲或删除RPC命令,直到它在每个输入源中都是最新的。
interface WatermarkNotifiable {
void started(String watermarkId);//KafkaSourceStartedWatermark watermark
void upToDate(String watermarkId);//KafkaSOurceUpToDateWatermark watermark
}
如果无法实现3中的基础设施,KafkaSource可以实现一个构造函数,指定可以传递给运营商的特殊水印事件,但这需要所有运营商依赖这些水印重新发出然后
其他不同的方法是不处理比标准更旧的命令。例如,命令具有条目时间戳。如果使用时间,则时间同步至关重要。
答案 0 :(得分:0)
创建一个新的Conmuter运算符类型。这就像一个来源。它由几个代表事件和命令主题的Source组成。它开始于“恢复”状态。在这种状态下,它从事件主题读取到最新。同时,对于命令,它存储或丢弃它们。一旦更新,它会考虑恢复并“打开”命令的方式。它可以作为源和运算符单独实现。
FlinkKafkaProducerXX不足以做到这一点,但它将是实现它的基础。