从与消费者相关的话题中获取最新价值,然后正常进行

时间:2019-04-18 14:58:05

标签: apache-kafka kafka-consumer-api apache-kafka-streams ksql

我们有一个Kafka生产者,它以很高的频率针对保留时间= 10小时的主题生成键控消息。这些消息是实时更新,使用的键是其值已更改的元素的ID。因此,该主题充当更改日志,并且将具有许多重复的键。

现在,我们要实现的是,当Kafka用户启动时,无论最近的已知状态(新用户,崩溃,重新启动等)如何,它都将以某种方式构造具有最新值的表主题中的所有键,然后像往常一样继续侦听新更新,保持Kafka服务器上的最小负载,并让使用者完成大部分工作。我们尝试了许多方法,但似乎没有最好的方法。

我们尝试了什么:

1个变更日志主题+ 1个紧凑主题:

  1. 生产者向事务中包装的两个主题发送相同的消息,以确保成功发送。
  2. 消费者启动并请求变更日志主题的最新偏移量。
  3. 从开始构造表开始就使用压缩的主题。
  4. 从请求的偏移量开始继续消耗变更日志。

缺点:

  • 即使将日志压缩频率设置为最高可能,在压缩主题中重复的可能性也很高。
  • x2 Kakfa服务器上的主题数。

KSQL:

使用KSQL,我们要么必须将KTable重写为主题,以便消费者可以看到它(其他主题),要么我们需要消费者使用KSQL SELECT到KSQL Rest Server并查询表(不是像Kafka API一样快速,高效)。

Kafka Consumer API:

消费者从头开始消费主题。效果很好,但是使用者必须消耗10小时的更改日志来构造最后一个值表。

Kafka流:

通过使用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),由于我们有大量的消费者,因此将产生大量主题。

根据我们的尝试,看起来我们需要增加服务器负载或使用者启动时间。是否没有“完美”的方式来实现我们正在尝试的目标?

谢谢。

2 个答案:

答案 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填充后要如何处理的情况下,我的答案将是:

  • 确保“变更日志主题”也已压缩。
  • 首先尝试使用KSQL。如果这不能满足您的需求,请尝试Kafka Streams。如果这不能满足您的需求,请尝试使用Kafka Consumer。

例如,如果应该对“表”数据进行任何状态处理,我将不会使用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,因为它提供了简便性。