Kafka Streams(Scala):无效的拓扑:尚未添加StateStore

时间:2019-05-08 18:13:04

标签: scala apache-kafka apache-kafka-streams

我有一个拓扑,其中有一个流A

从该流A,创建一个WindowedStore S

       A  --> [S]

然后我想根据S上的数据对A中的对象进行转换,并使这些转换后的对象(通过transformValues到达WindowStore逻辑。

为此,我为此创建了一个Transformer,创建了一个流A',并使窗口知道它(即,S将由A'而不是来自A)。

  A -> A'  --> [S]
       ^__read__|

但是我不能这样做,因为创建拓扑时会引发异常:

Caused by: org.apache.kafka.streams.errors.TopologyException: Invalid topology: StateStore storeName is not added yet.

有没有办法解决这个问题?这是一个限制吗?

代码示例:

  // A 
  val sessionElementsStream: KStream[K, SessionElement] = ...
  // A'
  val sessionElementsTransformed : KStream[K, SessionElementTransformed] = {
    // Here we use the sessionStoreName - but it is not added yet to the Topology
    sessionElementsStream.
      transformValues(sessionElementTransformerSupplier, sessionStoreName)
  }

  val sessionElementsWindowedStream: SessionWindowedKStream[K, SessionElementTransformed] = {
    sessionElementsTransformed.
      groupByKey(sessionElementTransformedGroupedBy).
      windowedBy(sessionWindows)
  }

  val sessionStore : KTable[Windowed[K], List[WindowedSession]] = 
    sessionElementsWindowedStream.aggregate(
        initializer = List.empty[WindowedSession])(
        aggregator = anAggregator, merger = aMerger)(materialized = getMaterializedMUPKSessionStore(sessionStoreName))     

最初的问题是,根据之前会话的值,我想在此之后更改会话。但是,如果我在会话后 之后在转换器中执行此操作,则可以更改这些转换后的会话并将其发送到下游-但它们不会在S中反映出它们的新状态-因此,对存储区将保留旧值。

Kafka Streams 2.1,Scala 2.12.4。 共同划分的主题。

更新

有一种方法可以在DSL中使用一个额外的主题:

  • 发送了A'to个主题
  • 从该主题创建builder.stream并从中建立商店。
  • 在定义转换之前定义存储(因此转换步骤可以使用存储,因为它已经在前面定义了)。

但是,在这里不得不使用额外的主题听起来很麻烦。没有其他更简单的解决方法了吗?

1 个答案:

答案 0 :(得分:0)

  

但是我不能这样做,因为创建拓扑时会引发异常:

Caused by: org.apache.kafka.streams.errors.TopologyException: Invalid topology: StateStore storeName is not added yet.

您似乎只是忘记将状态存储从字面上“添加”到处理拓扑,然后将状态存储附加(“使可用”)到您的Transformer

这是一个演示此内容的代码段(对不起,在Java中)。

将状态存储添加到拓扑:

final StreamsBuilder builder = new StreamsBuilder();
final StoreBuilder<KeyValueStore<String, Long> myStateStore =
    Stores.keyValueStoreBuilder(
             Stores.persistentKeyValueStore("my-state-store-name"),
             Serdes.String(),
             Serdes.Long())
          .withCachingEnabled();
builder.addStateStore(myStateStore);

将状态存储添加到您的Transformer

final KStream<String, Double> stream = builder.stream("your-input-topic", Consumed.with(Serdes.String(), Serdes.Double()));

final KStream<String, Long> transformedStream =
    stream.transform(new YourTransformer(myStateStore.name()), myStateStore.name());

当然,您的Transformer必须使用以下代码集成状态存储(此Transformer读取<String, Double>并写入String, Long>)。

class MyTransformer implements TransformerSupplier<String, Double, KeyValue<String, Long>> {

  private final String myStateStoreName;

  MyTransformer(final String myStateStoreName) {
    this.myStateStoreName = myStateStoreName;
  }

  @Override
  public Transformer<String, Double, KeyValue<String, Long>> get() {
    return new Transformer<String, Double, KeyValue<String, Long>>() {

      private KeyValueStore<String, Long> myStateStore;
      private ProcessorContext context;

      @Override
      public void init(final ProcessorContext context) {
        myStateStore = (KeyValueStore<String, Long>) context.getStateStore(myStateStoreName);
      }

   // ...
  }
}