KafkaStreams如何在流聚合中指定Serdes?

时间:2018-11-13 14:28:18

标签: apache-kafka apache-kafka-streams

我正在开发Kafka流应用程序,但在弄清楚如何使聚合工作方面遇到了麻烦。

我有一个KStream bankTransactions,其中键的类型为String,值的类型为JsonNode,所以我用

配置了应用程序的Serdes。
// Definition of the different Serdes used in the streams
final Serde<String> stringSerde = Serdes.String();
final Serde<JsonNode> jsonSerde = new JsonSerde();
final Serde<Long> longSerde = Serdes.Long();

config.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, stringSerde.getClass().getName());
config.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, jsonSerde.getClass().getName());

我想在KTable<String, Long>中聚合值,其中键是相同的,但是值是从我的Json中提取的Long s。

所以我首先写道:

KTable<String, Long> totalBalances = bankTransactions
        .groupByKey()
        .aggregate(
                () -> 0L,
                (key, transaction, balance) -> (Long)((Long)balance + transaction.get("amount").asLong()),
                Materialized.as("bank-total-balance")
        );

在运行时出现以下错误:

Caused by: org.apache.kafka.streams.errors.StreamsException:
A serializer (value: org.apache.kafka.connect.json.JsonSerializer) is not compatible to
the actual value type (value type: java.lang.Long).
Change the default Serdes in StreamConfig or provide correct Serdes via method parameters.

我知道Kafka在抱怨,因为我试图使用默认的Json serdes序列化Long。因此,从confluent's doc中读取内容,我尝试了

KTable<String, Long> totalBalances = bankTransactions
        .groupByKey()
        .aggregate(
                () -> 0L,
                (key, transaction, balance) -> (Long)((Long)balance + transaction.get("amount").asLong()),
                Materialized.as("bank-total-balance").withValueSerde(Serdes.Long())
        );

但是随后在编译时出现错误:

Error:(121, 89) java: incompatible types:
org.apache.kafka.common.serialization.Serde<java.lang.Long> cannot be converted
to org.apache.kafka.common.serialization.Serde<java.lang.Object>

我尝试了不同的方式来编写此代码(例如,使用Serdes.long()代替我的longSerdes,尝试对Materialize的类型进行参数化,甚至尝试将初始化器和聚合器写为函数,Java 7风格),但我不知道自己在做什么错。

所以我的问题很简单:当aggregate不是默认Serdes时应如何正确指定应该使用的Serdes?

1 个答案:

答案 0 :(得分:1)

似乎正确的语法如下:

KTable<String, Long> totalBalances = bankTransactions
        .groupByKey()
        .aggregate(
                () -> 0L,
                (key, transaction, balance) -> (Long)((Long)balance + transaction.get("amount").asLong()),
                Materialized.<String, Long, KeyValueStore<Bytes, byte[]>>as("bank-total-balances")
                        .withKeySerde(stringSerde)
                        .withValueSerde(longSerde)
        );

Materialize.之后的三种类型是键的一种,值的一种和一种用于实现KTable的存储的一种,并且这种类型不应更改。然后,我们可以定义用于写入此键值存储区的Serdes。

注意:我从github上的一个随机存储库中获得了这种语法,我仍然很乐意接受一个答案,并提供了一些文档支持的更精确答案。