Kafka Streams - 按时间戳/序列保留消息?

时间:2018-06-18 21:09:55

标签: java stream apache-kafka apache-kafka-streams

我正在接收Kafka流上的消息。它们由用户ID键入。当他们进来时,他们会得到一个序列号和时间戳。这些消息在15分钟后“过期”。用户可以根据给定时间(最多15分钟)或序列请求新消息。

我最初拥有的是这样的:

`       StreamsBuilder streamsBuilder = new StreamsBuilder();

  KStream<String, Message> inboundStream = streamsBuilder.stream("incoming.topic");
  messageSupplier = Stores.persistentKeyValueStore("user.messages");

  KTable<String, MessageCache> messageTable = inboundStream
      .filter(this::userExists)
      .peek(this::recordInboundMessage)
      .map(this::markMessage)       // add sequence/timestamp
      .groupByKey()
      .aggregate(this::createMessageCache,
              this::addMessageToMessageCache,
              Materialized.as(messageSupplier));

  // ---> Some other setup stuff, then start the streams

`

MessageCache保存消息列表(当我们将消息添加到缓存时删除过期的消息)。当我收到消息请求时,我会浏览列表并过滤掉相应的消息。

我原以为我可以使用其中一种窗口策略,但找不到实际持有消息列表的示例。

这是最好的方法吗?或者我错过了一些可以让这更容易/更好的东西?

1 个答案:

答案 0 :(得分:0)

  

这是最好的方法吗?还是我错过了一些使它更容易/更好的事情?

我认为您有一个使用本机Java类的简单解决方案,可以有效地将流应用程序与您的代码联系起来...为了简单起见,有很多话要说!我唯一看到的缺点是,如果事件发生率太高,用户缓存是否可能超出内存大小。另外,如果您需要容错,则流式应用程序将在发生故障的情况下在另一个应用程序实例上重新构建状态存储的内容。但是,如果这不是一个问题,那就去吧!

但是,就如何在流应用程序上下文中执行此操作而言,您可以做一些调整:

  1. 定义要支持的用户查询的粒度。分钟?秒?为了讨论起见,我们说分钟。根据该粒度对流进行窗口化。

  2. 定义一个类似于您所拥有的累加器,该累加器将接受用户记录并将其添加到列表中。类似于UserRecordGroup,其ListUserRecord,以及方法add(UserEvent evt),该方法会将UserRecord附加到List

然后,您可以构建自己的流应用程序,例如:

KStream<String, Message> inboundStream = streamsBuilder.stream("incoming.topic");
 Materialized<String, UserRecordGroup, WindowStore<Bytes, byte[]>> userStore =
 Materialized.<String, UserRecordGroup, WindowStore<Bytes,byte[]>>as("user.messages")
  .withValueSerde(/* your serializers here */);


KTable<String, MessageCache> messageTable = inboundStream
  .filter(this::userExists)
  .peek(this::recordInboundMessage)
  .map(this::markMessage)       // add sequence/timestamp
  .groupByKey()
  .windowedBy(TimeWindows.of(ONE_MINUTE_IN_MS))
  .aggregate(UserRecordGroup::new,
            (key, value, agg) -> { agg.add(value); },
             userStore);

最后,当您想从商店中提供查询时,可以

ReadOnlyWindowStore<Integer, UserRecordGroup> store =
   streams.store("user.messages", QueryableStoreTypes.windowStore());
WindowStoreIterator<UserRecordGroup> windowIterator = 
     store.fetch(pathHash, startTimestamp, endTimeStamp);

您的迭代器将包含不同窗口的所有记录的列表;将这些列表合并在一起,您将获得startTimestamp和endTimestamp之间用户活动的描述。