用KStream语义重新组合

时间:2019-09-06 20:56:27

标签: apache-kafka apache-kafka-streams

使用kafka-streams,我想按某个键S对元素E的流K1进行分组,同时将同一键的所有值聚合到一个合并的结果{{1}中}。这将产生一个KTable AGG

根据汇总结果,应将值重新划分到另一个KTable T1中,并按从汇总结果T2中获取的键K2进行分组。因此,汇总结果应为下一次重组生成密钥。

最后,我只对KTable AGG感兴趣,其中的键是T2,值是K2

但是,这不起作用。我只有一个KTable AGG作为最后一个值。不是每个键T

的值

我知道聚合的结果只会在一段时间后转发,因此我已经尝试将K2降低到1,但无济于事。

我还尝试使用commit.interval.ms并将中间结果写入流中,但同样没有成功。

through

对于包含以下值的流val finalTable = streamBuilder.kstream("streamS") .groupBy{ k, v -> createKey1(k, v) } .aggregate( { Agg.empty() }, { k, v, previousAgg -> Agg.merge(previousAgg, v) }) .toStream() // .through("table1") .groupBy { k1, agg -> agg.createKey2()} .reduce{ _, agg -> agg }
S
key1="123", id="1", startNewGroup="false"
key1="234", id="2", startNewGroup="false"
key1="123", id="3", startNewGroup="false"
key1="123", id="4", startNewGroup="true"
key1="234", id="5", startNewGroup="false"
key1="123", id="6", startNewGroup="false"
key1="123", id="7", startNewGroup="false"

我希望最终结果是一个具有以下最新键值的KTable:
key1="123", id="8", startNewGroup="true"
key: 123-1, value: 'key1="123", key2="123-1", ids="1,3"'
key: 234-2, value: 'key1="234", key2="234-2", ids="2,5"'
key: 123-4, value: 'key1="123", key2="123-4", ids="4,6,7"'

原始元素流key: 123-8, value: 'key1="123", key2="123-8", ids="8"'首先按S分组,其中聚合结果包含分组键key1,并添加包含{的组合的额外字段key1 {1}}与第一次出现的key2
每当聚合收到的key1设置为id的值时,它将返回一个startNewGroup字段设置为新的truekey2的聚合价值,有效地创建一个新的子组。
在第二次重新组合中,我们通过key1字段进行简单分组。

但是,我们真正观察到的是以下情况:
id
key2

1 个答案:

答案 0 :(得分:0)

对于您的用例,最好使用Processor API。处理器API可以轻松地与Kafka Streams DSL(Processor API integration)结合使用。

您必须实现自定义Transformer,该自定义This将为状态存储中的特定密钥聚合您的消息。当startNewGroup=true消息到达时,密钥的 old 消息将转发到下游,并且 new 聚合将开始

您的Sample Transformer可能如下所示:

import org.apache.kafka.streams.kstream.Transformer
import org.apache.kafka.streams.processor.ProcessorContext
import org.apache.kafka.streams.state.KeyValueStore

case class CustomTransformer(storeName: String) extends Transformer[String, Value, Agg] {

  private var stateStore: KeyValueStore[String, Agg] = null
  private var context: ProcessorContext = null

  override def init(context: ProcessorContext): Unit = {
    this.context = context
    stateStore = context.getStateStore(storeName).asInstanceOf[KeyValueStore[String, Agg]]
  }

  override def transform(key: String, value: Value): Agg = {
    val maybeAgg = Option(stateStore.get(key))

    if (value.startNewGroup) {
      maybeAgg.foreach(context.forward(key, _))
      stateStore.put(key, Agg(value))
    }
    else
      stateStore.put(key, maybeAgg.map(_.merge(value)).getOrElse(Agg(value)))
    null
  }

  override def close(): Unit = {}
}