Kafka Streams - 状态存储可能已迁移到另一个实例

时间:2018-03-15 10:09:17

标签: apache-kafka-streams

我正在编写一个基本应用程序来测试Kafka Streams的Interactive Queries功能。这是代码:

public static void main(String [] args){

    StreamsBuilder builder = new StreamsBuilder();

    KeyValueBytesStoreSupplier waypointsStoreSupplier = Stores.persistentKeyValueStore("test-store");
    StoreBuilder waypointsStoreBuilder = Stores.keyValueStoreBuilder(waypointsStoreSupplier, Serdes.ByteArray(), Serdes.Integer());

    final KStream<byte[], byte[]> waypointsStream = builder.stream("sample1");

    final KStream<byte[], TruckDriverWaypoint> waypointsDeserialized =  waypointsStream
                                                                        .mapValues(CustomSerdes::deserializeTruckDriverWaypoint)
                                                                        .filter((k,v) -> v.isPresent())
                                                                        .mapValues(Optional::get);

    waypointsDeserialized.groupByKey().aggregate(
            () -> 1,
            (aggKey, newWaypoint, aggValue) -> {

                aggValue = aggValue + 1;
                return aggValue;

            }, Materialized.<byte[], Integer, KeyValueStore<Bytes, byte[]>>as("test-store").withKeySerde(Serdes.ByteArray()).withValueSerde(Serdes.Integer())
    );

    final KafkaStreams streams = new KafkaStreams(builder.build(), new StreamsConfig(createStreamsProperties()));

    streams.cleanUp();
    streams.start();    

    ReadOnlyKeyValueStore<byte[], Integer> keyValueStore = streams.store("test-store", QueryableStoreTypes.keyValueStore());

    KeyValueIterator<byte[], Integer> range = keyValueStore.all();
    while (range.hasNext()) {
        KeyValue<byte[], Integer> next = range.next();
        System.out.println(next.value);

    }

    Runtime.getRuntime().addShutdownHook(new Thread(streams::close));

}


protected static Properties createStreamsProperties() {

    final Properties streamsConfiguration = new Properties();

    streamsConfiguration.put(StreamsConfig.APPLICATION_ID_CONFIG, "random167");
    streamsConfiguration.put(StreamsConfig.CLIENT_ID_CONFIG, "client-id");
    streamsConfiguration.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
    streamsConfiguration.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
    streamsConfiguration.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, Serdes.String().getClass().getName());
    streamsConfiguration.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, Serdes.Integer().getClass().getName());
    //streamsConfiguration.put(StreamsConfig.COMMIT_INTERVAL_MS_CONFIG, 10000);

    return streamsConfiguration;
}

所以我的问题是,每次运行时我都会遇到同样的错误:

Exception in thread "main" org.apache.kafka.streams.errors.InvalidStateStoreException: the state store, test-store, may have migrated to another instance.

我只运行了一个应用程序实例,而我正在使用的主题只有一个分区。

知道我做错了吗?

4 个答案:

答案 0 :(得分:8)

看起来你有竞争条件。来自KafkaStreams::start()的kafka流javadoc,它说:

  

通过启动所有线程来启动KafkaStreams实例。期望在客户端的生命周期中仅调用此函数一次。   因为线程是在后台启动的,所以此方法不会阻止。

https://kafka.apache.org/10/javadoc/index.html?org/apache/kafka/streams/KafkaStreams.html

您在streams.store()之后立即致电streams.start(),但我打赌您处于尚未完全初始化状态。

由于此代码似乎只是用于测试,因此在其中添加Thread.sleep(5000)或其他内容并开始使用。 (这不是生产的解决方案)根据您对主题的输入率,这可能会给商店一些时间开始填充事件,以便您的KeyValueIterator实际上有一些东西要处理/打印。

答案 1 :(得分:1)

可能不适用于OP,但可能会帮助其他人:

在尝试检索KTable的存储时,请确保首先存在KTable的主题,否则您将获得此异常。

答案 2 :(得分:0)

在使用商店之前,我未能致电Storebuilder

答案 3 :(得分:0)

<块引用>

通常发生这种情况有两个原因:

本地 KafkaStreams 实例尚未准备好(即尚未进入 运行时状态 RUNNING,请参阅运行时状态信息)及其 还不能查询本地状态商店。本地 KafkaStreams 实例已准备好(例如,在运行时状态为 RUNNING),但特定的 state store 刚刚在后台迁移到另一个实例。 这可能特别发生在分布式系统的启动阶段 应用程序或当您添加/删除应用程序实例时。

https://docs.confluent.io/platform/current/streams/faq.html#handling-invalidstatestoreexception-the-state-store-may-have-migrated-to-another-instance

最简单的方法是在调用 KafkaStreams#store() 时防范 InvalidStateStoreException:

// Example: Wait until the store of type T is queryable.  When it is, return a reference to the store.
public static <T> T waitUntilStoreIsQueryable(final String storeName,
                                              final QueryableStoreType<T> queryableStoreType,
                                              final KafkaStreams streams) throws InterruptedException {
  while (true) {
    try {
      return streams.store(storeName, queryableStoreType);
    } catch (InvalidStateStoreException ignored) {
      // store not yet ready for querying
      Thread.sleep(100);
    }
  }
}