未指定默认Serdes并使用自定义的Serdes-> org.apache.kafka.streams.errors.StreamsException时,通过KStream进行的映射操作失败

时间:2018-08-26 12:15:20

标签: apache-kafka apache-kafka-streams

自从我使用Json值以来,我还没有设置默认的Serdes。

我处理KStream,并使用必需的spring和product(json)serdes来使用它,但下一步(映射操作)失败:

val props = Properties()
props[StreamsConfig.APPLICATION_ID_CONFIG] = applicationName
props[StreamsConfig.BOOTSTRAP_SERVERS_CONFIG] = kafkaBootstrapServers

val productSerde: Serde<Product> = Serdes.serdeFrom(JsonPojoSerializer<Product>(), JsonPojoDeserializer(Product::class.java))

builder.stream(INVENTORY_TOPIC, Consumed.with(Serdes.String(), productSerde))
            .map { key, value ->
                KeyValue(key, XXX)
            }
            .aggregate(...)

如果我删除了地图操作,执行就可以了。

我还没有找到一种方法来指定map()的Serdes,怎么做?

错误:

Caused by: org.apache.kafka.streams.errors.StreamsException: A serializer (key: org.apache.kafka.common.serialization.ByteArraySerializer / value: org.apache.kafka.common.serialization.ByteArraySerializer) is not compatible to the actual key or value type (key type: java.lang.String / value type: com.codependent.kafkastreams.inventory.dto.Product). Change the default Serdes in StreamConfig or provide correct Serdes via method parameters.
    at org.apache.kafka.streams.processor.internals.SinkNode.process(SinkNode.java:92)

2 个答案:

答案 0 :(得分:0)

多个问题:

  1. 致电map()后,您致电groupByKey().aggregate()。这将触发数据重新分区,因此在将map()数据写入内部主题以进行数据重新分区之后。因此,您还需要在Serde中提供相应的groupByKey()

  2. 但是,因为您没有修改密钥,所以实际上应该调用mapValues(),以避免不必要的重新分区。

  3. 请注意,您需要为每个不应使用配置中默认Serde的运算符提供SerdeSerde不沿下游传递,而是操作员就地覆盖。 (Kafka 2.1正在进行改进。)

答案 1 :(得分:0)

如果有人像我一样来到这里,这里有一个演示 Matthias 解决方案的实际示例。由于 ClassCastException,以下代码将失败。

builder.stream(
        "my-topic",
        Consumed.with(Serdes.String(), customSerde))
        .map((k, v) -> {
            // You've modified the key. Get ready for an error!
            String newKey = k.split(":")[0];
            return KeyValue.pair(newKey.toString(), v);
        })
        .groupByKey()
        .aggregate(
            MyAggregate::new,
            (key, data, aggregation) -> {
                // You'll never reach this block due to an error similar to:
                // ClassCastException while producing data to topic
                return aggregation.updateFrom(shot);
            },
            Materialized.<String, MyAggregate> as(storeSupplier)
                .withKeySerde(Serdes.String())
                .withValueSerde(aggregateSerde)
        )

正如 Matthias 所提到的,“您需要在 groupByKey 中提供相应的 Serdes”。方法如下:

builder.stream(
        "my-topic",
        Consumed.with(Serdes.String(), customSerde))
        .map((k, v) -> {
            // You've modified the key. Get ready for an error!
            String newKey = k.split(":")[0];
            return KeyValue.pair(newKey.toString(), v);
        })
        // Voila, you've specified the serdes required by the new internal
        // repartition topic and you can carry on with your work
        .groupByKey(Grouped.with(Serdes.String(), customSerde))
        .aggregate(
            MyAggregate::new,
            (key, data, aggregation) -> {
                // You'll never reach this block due to an error similar to:
                // ClassCastException while producing data to topic
                return aggregation.updateFrom(shot);
            },
            Materialized.<String, MyAggregate> as(storeSupplier)
                .withKeySerde(Serdes.String())
                .withValueSerde(aggregateSerde)
        )