我们有一个Kafka生产者,它以很高的频率针对保留时间= 10小时的主题生成键控消息。这些消息是实时更新,使用的键是其值已更改的元素的ID。因此,该主题充当更改日志,并且将具有许多重复的键。
现在,我们要实现的是,当Kafka用户启动时,无论最近的已知状态(新用户,崩溃,重新启动等)如何,它都将以某种方式构造具有最新值的表主题中的所有键,然后像往常一样继续侦听新更新,保持Kafka服务器上的最小负载,并让使用者完成大部分工作。我们尝试了许多方法,但似乎没有最好的方法。
我们尝试了什么:
缺点:
使用KSQL,我们要么必须将KTable重写为主题,以便消费者可以看到它(其他主题),要么我们需要消费者使用KSQL SELECT
到KSQL Rest Server并查询表(不是像Kafka API一样快速,高效)。
消费者从头开始消费主题。效果很好,但是使用者必须消耗10小时的更改日志来构造最后一个值表。
通过使用KTables如下:
KTable<Integer, MarketData> tableFromTopic = streamsBuilder.table("topic_name", Consumed.with(Serdes.Integer(), customSerde));
KTable<Integer, MarketData> filteredTable = tableFromTopic.filter((key, value) -> keys.contains(value.getRiskFactorId()));
Kafka Streams将在每个KTable的Kafka服务器上创建1个主题(名为{consumer_app_id}-{topic_name}-STATE-STORE-0000000000-changelog
),由于我们有大量的消费者,因此将产生大量主题。
根据我们的尝试,看起来我们需要增加服务器负载或使用者启动时间。是否没有“完美”的方式来实现我们正在尝试的目标?
谢谢。
答案 0 :(得分:2)
通过使用KTables,Kafka Streams将在每个KTable的Kafka服务器上创建1个主题,由于我们有大量的消费者,因此将产生大量主题。
如果您只是将现有主题读入KTable
(通过StreamsBuilder#table()
),则Kafka Streams不会再创建其他主题。对于KSQL也是一样。
如果您可以弄清楚您要对KTable做些什么,这将有所帮助。显然,您正在做的事情确实会导致创建其他主题?
1个changelog主题+ 1个紧凑主题:
您为什么要考虑两个单独的主题?通常,更改日志主题应始终压缩。鉴于您的用例描述,我看不出为什么不应该这样:
现在,我们要实现的是,当Kafka用户启动时,无论最近的已知状态(新用户,崩溃,重新启动等)如何,它都将以某种方式构造具有最新值的表主题中的所有键,然后像往常一样继续监听新的更新[...]
因此,压缩对于您的用例将非常有用。它还可以防止您描述此问题:
消费者从头开始消费主题。效果很好,但是使用者必须消耗10小时的更改日志来构造最后一个值表。
请注意,要重建最新的表值,Kafka Streams,KSQL和Kafka Consumer的所有三个必须完全(从头到尾)阅读表的基础主题。如果未压缩该主题,则可能确实需要花费很长时间,具体取决于数据量,主题保留设置等。
根据我们的尝试,看起来我们需要增加服务器负载或使用者启动时间。是否没有“完美”的方式来实现我们正在尝试的目标?
在不了解您的用例的情况下,尤其是在KTable填充后要如何处理的情况下,我的答案将是:
例如,如果应该对“表”数据进行任何状态处理,我将不会使用Kafka Consumer,因为Kafka Consumer缺乏用于容错状态处理的内置功能。</ p>
答案 1 :(得分:0)
消费者从头开始消费主题。这工作 完美,但是使用者必须消耗10个小时的更改日志以 构造最后一个值表。
在您的应用程序第一次启动时,您所说的是正确的。
为避免每次重新启动时都发生这种情况,请将键值数据存储在文件中。
例如,您可能要使用永久性地图(如MapDB)。
由于您给了使用者group.id
并定期或在将每条记录存储在映射中后提交了偏移量,因此下次应用程序重新启动时,它将从该{{1 }}。
因此,花费大量时间的问题仅在最初(第一次)期间发生。只要有了文件,就不需要从头开始使用。
如果文件不存在或被删除,则只需group.id
中的seekToBeginning
并重新构建即可。
在某个地方,您需要存储此键值以进行检索,为什么它不能成为持久存储?
如果出于任何原因要使用Kafka流,则替代方法(不是上述的简单)将使用持久支持的存储。 / p>
例如,持久性全局存储。
KafkaConsumer
P.S:目录中将有一个名为streamsBuilder.addGlobalStore(Stores.keyValueStoreBuilder(Stores.persistentKeyValueStore(topic), keySerde, valueSerde), topic, Consumed.with(keySerde, valueSerde), this::updateValue);
的文件,用于存储偏移量。如果该主题在中间被删除,您将得到.checkpoint
。您可能想避免这种情况,也许可以使用OffsetOutOfRangeException
有关更多信息,请参见https://stackoverflow.com/a/57301986/2534090。
最后,
为此,最好将Consumer与永久文件一起使用而不是Streams,因为它提供了简便性。