为什么我的Kafka Streams拓扑不能正确重放/重新处理?

时间:2018-01-16 18:41:15

标签: apache-kafka apache-kafka-streams

我有一个如下所示的拓扑:

KTable<ByteString, User> users = topology.table(USERS);

KStream<ByteString, JoinRequest> joinRequests = topology.stream(JOIN_REQUESTS)
    .mapValues(entityTopologyProcessor::userNew)
    .to(USERS);

topology.stream(SETTINGS_CONFIRM_REQUESTS)
    .join(users, entityTopologyProcessor::userSettingsConfirm)
    .to(USERS);

topology.stream(SETTINGS_UPDATE_REQUESTS)
    .join(users, entityTopologyProcessor::userSettingsUpdate)
    .to(USERS);

在运行时,此拓扑可正常工作。用连接请求创建用户。他们通过设置确认请求确认其设置。他们使用设置更新请求更新其设置。

但是,重新处理此拓扑不会产生原始结果。具体来说,设置更新joiner没有看到设置确认joiner导致的用户,即使在时间戳方面,从创建用户开始经过很多秒,到用户确认到用户更新时间的时间他们的设置。

我很茫然。我已经尝试关闭用户表上的缓存/日志记录。不知道如何正确地进行这种重新处理。

2 个答案:

答案 0 :(得分:2)

KStream-KTable连接不是100%确定性的(并且可能永远不会变为100%确定性)。我们意识到了这个问题并讨论了解决方案,至少可以缓解这个问题。

一个问题是,如果消费者从代理获取,我们无法轻易控制代理返回数据的主题和/或分区。根据我们从代理接收数据的顺序,结果可能略有不同。

一个相关问题:https://issues.apache.org/jira/browse/KAFKA-3514

此博客文章也可能有所帮助:https://www.confluent.io/blog/crossing-streams-joins-apache-kafka/

答案 1 :(得分:0)

我能够通过用以下代码替换代码中的代码来部分解决我的问题:

KTable<ByteString, User> users = topology.table(JOIN_REQUESTS)
    .mapValue(entityTopologyProcessor::user)
    .leftJoin(topology
                 .stream(CONFIRM_SETTINGS_REQUESTS)
                 .groupByKey()
                 .reduce((a, b) -> b),
              entityTopologyProcessor::confirmSettings)
    .leftJoin(topology
                 .stream(SETTINGS_UPDATE_REQUESTS)
                 .groupByKey()
                 .reduce(entityTopologyProcessor::settingsUpdateReduce),
              entityTopologyProcessor::settingsUpdate);

此解决方案利用了所有表 - 表连接都是确定性的事实。在重新处理期间,结果状态可能暂时不正确,但是一旦拓扑被捕获,最终值就是正确的(给定结果的最终时间戳仍然不是确定性的)。一般来说,这种方法将给定实体(在此示例中为用户)的所有事件(在此示例中:加入请求,确认设置请求,设置更新请求)分组到单个任务中,并将其累积连接到单个产品中。此示例可以通过在结尾处连接另一个流来删除事件来扩展删除事件。

除了这种方法,通常,编写可重新处理的拓扑需要在两个维度上考虑拓扑:实时和重新处理时间。从Kafka Streams 1.0.0开始,这对于开发人员来说是一门艺术。