Java 这种聚合在卡夫卡流中是如何工作的?

Java 这种聚合在卡夫卡流中是如何工作的?,java,json,apache-kafka,apache-kafka-streams,Java,Json,Apache Kafka,Apache Kafka Streams,我是阿帕奇·卡夫卡的新手。我阅读了steam应用程序的代码,偶然发现了聚合操作。我试着自己去理解它,如果我的解释是正确的,我需要确认 下面提供了从主题和聚合中读取的代码片段 // json Serde final Serializer<JsonNode> jsonSerializer = new JsonSerializer(); final Deserializer<JsonNode> jsonDeserializer = new JsonDeserializer();

我是阿帕奇·卡夫卡的新手。我阅读了steam应用程序的代码,偶然发现了聚合操作。我试着自己去理解它,如果我的解释是正确的,我需要确认

下面提供了从主题和聚合中读取的代码片段

// json Serde
final Serializer<JsonNode> jsonSerializer = new JsonSerializer();
final Deserializer<JsonNode> jsonDeserializer = new JsonDeserializer();
final Serde<JsonNode> jsonSerde = Serdes.serdeFrom(jsonSerializer, jsonDeserializer);


KStreamBuilder builder = new KStreamBuilder();

// read from the topic 'bank-transactions' as `KStream`. I provided the producer below 
KStream<String, JsonNode> bankTransactions = builder.stream(Serdes.String(), jsonSerde, "bank-transactions");

// we define the grouping and aggregation here 
KTable<String, JsonNode> bankBalance = bankTransactions.groupByKey(Serdes.String(), jsonSerde)
    .aggregate(
            () -> initialBalance,
            (key, transaction, balance) -> newBalance(transaction, balance),
            jsonSerde,
            "bank-balance-agg"
    );
初始余额如下所示:

public static ProducerRecord<String, String> newRandomTransaction(String name) {
    // creates an empty json {}
    ObjectNode transaction = JsonNodeFactory.instance.objectNode();

    Integer amount = ThreadLocalRandom.current().nextInt(0, 100);

    // Instant.now() is to get the current time using Java 8
    Instant now = Instant.now();

    // we write the data to the json document
    transaction.put("name", name);
    transaction.put("amount", amount);
    transaction.put("time", now.toString());

    return new ProducerRecord<>("bank-transactions", name, transaction.toString());
}
// create the initial json object for balances
ObjectNode initialBalance = JsonNodeFactory.instance.objectNode();

initialBalance.put("count", 0);
initialBalance.put("balance", 0);
initialBalance.put("time", Instant.ofEpochMilli(0L).toString());
newBalance
方法获取事务和余额并返回新余额

private static JsonNode newBalance(JsonNode transaction, JsonNode balance) {
    // create a new balance json object
    ObjectNode newBalance = JsonNodeFactory.instance.objectNode();

    newBalance.put("count", balance.get("count").asInt() + 1);
    newBalance.put("balance", balance.get("balance").asInt() + transaction.get("amount").asInt());

    Long balanceEpoch = Instant.parse(balance.get("time").asText()).toEpochMilli();
    Long transactionEpoch = Instant.parse(transaction.get("time").asText()).toEpochMilli();

    Instant newBalanceInstant = Instant.ofEpochMilli(Math.max(balanceEpoch, transactionEpoch));
    newBalance.put("time", newBalanceInstant.toString());

    return newBalance;
}
我有两个关于分组和聚合的问题

a。
groupByKey
是否通过
Serdes.String()
进行分组,而
jsonSerde
是否仅对steam数据执行序列化和反序列化?
Serdes.String()
newRandomTransaction
方法中的名称字符串

b。我的断言是
行的
(key,transaction,balance)->newBalance(transaction,balance)
聚合
函数中的
键,transaction
,从
银行交易
主题读取
余额
来自上一行的
初始余额。对吗

我在调试应用程序时也感到困惑,尽管它可以无缝运行

groupByKey是否通过Serdes.String()进行分组,而jsonSerde仅对steam数据执行序列化和反序列化

是的,groupByKey是按键分组的,这些键可以作为字符串进行反序列化和比较

我的断言是行(key,transaction,balance)->newBalance(transaction,balance)的聚合函数中的key,transaction是从bank transactions主题读取的,余额来自前一行的initialBalance

差不多。初始值设定项位于第一个参数上,是的,但聚合结果将在应用程序的整个执行过程中向前推进,无休止地聚合

换句话说,您总是从
初始余额
开始,然后对于每个相同的键,您将该
事务
的余额添加到该键当前累计的
余额
。如果您尚未看到该键被重复,则只有在该键被添加到初始余额中时才会出现


是的,您的输入主题是由KStreams
builder.stream
method

指定的,我确信我在这里的理解很幼稚,但这是我第一次使用卡夫卡(也是流),也许举个例子会有所帮助?另外请注意,重新分区主题仅在必要时创建——对于问题中的代码段,不需要重新分区。嗨,cricket_007,我理解您的答案,并接受了它。但是,一些例子会做得更好,因为在我看来,《卡夫卡》的文档需要更好。@Matthias我将把这个例子留给你;)@如果您认为文档不足,请向AK投稿并打开PR以改进文档!