是否可以从kafka消息中获取消息密钥的最新值

时间:2020-04-01 06:37:42

标签: apache-kafka apache-kafka-streams spring-kafka confluent-platform ksqldb

假设同一消息密钥的值不同。

例如:

{
userid: 1,
email: user123@xyz.com }

{
userid: 1,
email: user456@xyz.com }

{
userid: 1,
email: user789@xyz.com }

在上述情况下,我只希望用户更新最新值,即“ user789@xyz.com”。

我的kafka流应该只给我第三个值,而不是前两个值。

3 个答案:

答案 0 :(得分:7)

由于您尚未指定特定的客户端,因此我将向您展示如何使用ksqlDB和新添加的功能LATEST_BY_OFFSET完成此操作。

首先,我用源数据填充主题:

kafkacat -b broker:29092 -P -t test_topic -K: <<EOF
1:{ "userid": 1, "email": "user123@xyz.com" }
1:{ "userid": 1, "email": "user456@xyz.com" }
1:{ "userid": 1, "email": "user789@xyz.com" }
EOF

然后在ksqlDB中首先将此模型建模为事件流:

ksql> CREATE STREAM USER_UPDATES (USERID INT, EMAIL VARCHAR) WITH (KAFKA_TOPIC='test_topic', VALUE_FORMAT='JSON');

 Message
----------------
 Stream created
----------------

ksql> SET 'auto.offset.reset' = 'earliest';                                                                                                                                                                                                                                         [35/60]
Successfully changed local property 'auto.offset.reset' to 'earliest'. Use the UNSET command to revert your change.
ksql> SELECT ROWKEY, USERID, EMAIL FROM USER_UPDATES EMIT CHANGES LIMIT 3;
+---------+---------+-----------------+
|ROWKEY   |USERID   |EMAIL            |
+---------+---------+-----------------+
|1        |1        |user123@xyz.com  |
|1        |1        |user456@xyz.com  |
|1        |1        |user789@xyz.com  |

现在,我们可以告诉ksqlDB采取以下事件流,并直接向我们提供最新值(基于偏移量):

ksql> SELECT USERID, LATEST_BY_OFFSET(EMAIL) FROM USER_UPDATES GROUP BY USERID EMIT CHANGES;
+--------------------+--------------------+
|USERID              |KSQL_COL_1          |
+--------------------+--------------------+
|1                   |user789@xyz.com     |

Press CTRL-C to interrupt

或更有用,作为ksqlDB中的物化状态:

CREATE TABLE USER_LATEST_STATE AS 
    SELECT USERID, LATEST_BY_OFFSET(EMAIL) AS EMAIL 
      FROM USER_UPDATES 
     GROUP BY USERID 
     EMIT CHANGES;

此表仍然受Kafka主题的更改驱动,但可以直接查询当前的状态,或者从现在开始(“拉查询”):

ksql> SELECT EMAIL FROM USER_LATEST_STATE WHERE ROWKEY=1;
+--------------------+
|EMAIL               |
+--------------------+
|user789@xyz.com     |
Query terminated
ksql>

或随着状态的演变而变化(“推送查询”):

ksql> SELECT EMAIL FROM USER_LATEST_STATE WHERE ROWKEY=1 EMIT CHANGES;
+--------------------+
|EMAIL               |
+--------------------+
|user789@xyz.com     |

[ query continues indefinitely ]

asciicast

答案 1 :(得分:1)

似乎您想在进一步处理之前缓冲记录。自从流式传输以来,您拥有不断增长的无限数据集,因此您永远不知道是否要等待更多记录或刷新缓冲区以进行进一步处理。您能否添加有关如何处理这些记录的更多详细信息?

您可以引入一个附加参数,它是刷新缓冲区之前要等待的最长时间。要对此进行存档,可以use a Session window or a Tumbling window,也可以使用records cache in associate with a commit interval,也可以使用Kafka低杠杆处理器API来实现。

这是示例代码,展示了如何使用Tumbling窗口将其归档,以在1小时时间窗口内汇总和隐藏所有userId信息,接受10分钟后的事件,然后将抑制的事件发送给下游处理器(如果您may not get the final results一直使用它,直到出现新事件为止):

userInfoKStream
    .groupByKey()
    .windowedBy(TimeWindows.of(Duration.ofHours(1)).grace(Duration.ofMinutes(10)))
    .aggregate(() -> "", (userId, newValue, currentValue) -> newValue)
    .suppress(Suppressed.untilWindowCloses(Suppressed.BufferConfig.unbounded()))
    .toStream()
    .foreach((userId, value) -> {});

答案 2 :(得分:-1)

您需要Kafka log compaction。简而言之,如果您希望主题仅保留特定键的最后一个值,则应设置属性log.cleanup.policy=compact。您可以找到有关here的更多信息。