卡夫卡流加入

时间:2017-09-25 19:09:49

标签: java join java-8 apache-kafka-streams

我有2个kafka主题 - recommendationsclicks。第一个主题具有由唯一ID(称为recommendationsId)键入的建议对象。每个产品都有一个用户可以点击的URL。

clicks主题获取通过推荐给用户的产品网址的点击生成的消息。如此设置,这些点击消息也由recommendationId键入。

请注意

  1. 建议与点击之间的关系是一对多的。建议可能会导致多次点击,但点击始终与单个推荐相关联。

  2. 每个点击对象都有一个相应的推荐对象。

  3. 点击对象的时间戳晚于推荐对象。

  4. 推荐与相应点击之间的差距可能是几秒到几天(比如最多7天)。

  5. 我的目标是使用Kafka stream join加入这两个主题。我不清楚的是我是否应该使用KStream x KStream连接或KStream x KTable连接。

    我通过KStream x KTable表加入clicks流来实现recommendations加入。但是,如果在加入者开始之前生成建议并且在加入者开始后点击到达,我将无法看到任何加入的点击建议对。

    我使用正确的加入吗?我应该使用KStream x KStream加入吗?如果是这样,为了能够在过去7天内加入带有推荐的点击,我应该将窗口大小设置为7天吗?在这种情况下,我还需要设置“保留”期吗?

    我执行KStream x KTable加入的代码如下。请注意,我已经定义了类RecommendationsClick及其对应的serde。点击消息只是普通String(网址)。此URL字符串与Recommendations对象连接以创建Click对象,该对象将发送到jointTopic

    public static void main(String[] args){
        if(args.length!=4){
          throw new RuntimeException("Expected 3 params: bootstraplist clickTopic recsTopic jointTopic");
        }
    
        final String booststrapList = args[0];
        final String clicksTopic = args[1];
        final String recsTopic = args[2];
        final String jointTopic = args[3];
    
        Properties config = new Properties();
        config.put(StreamsConfig.APPLICATION_ID_CONFIG, "my_joiner_id");
        config.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, booststrapList);
        config.put(StreamsConfig.KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
        config.put(StreamsConfig.VALUE_SERDE_CLASS_CONFIG, JoinSerdes.CLICK_SERDE.getClass().getName());
    
        KStreamBuilder builder = new KStreamBuilder();
    
        // load clicks as KStream
        KStream<String, String> clicksStream = builder.stream(Serdes.String(), Serdes.String(), clicksTopic);
    
        // load recommendations as KTable
        KTable<String, Recommendations> recsTable = builder.table(Serdes.String(), JoinSerdes.RECS_SERDE, recsTopic);
    
        // join the two
        KStream<String, Click> join = clicksStream.leftJoin(recsTable, (click, recs) -> new Click(click, recs));
    
        // emit the join to the jointTopic
        join.to(Serdes.String(), JoinSerdes.CLICK_SERDE, jointTopic);
    
        // let the action begin
        KafkaStreams streams = new KafkaStreams(builder, config);
        streams.start();
      }
    

    只要在加入者(上述程序)运行后生成了建议和点击,这样就可以正常工作。但是,如果在运行joiner之前,生成了建议的点击到达,我看不到任何连接发生。我该如何解决这个问题?

    如果解决方案是使用KStream x KSTream加入,请帮助我了解应选择的窗口大小以及要选择的保留期限。

1 个答案:

答案 0 :(得分:7)

您的整体观察是正确的。从概念上讲,您可以通过两种方式获得正确的结果。如果你使用流表连接,你有两个缺点(这可能会在未来的Kafka版本中重新审视和改进)

  • 您已经提到,如果在相应推荐之前处理了点击,则(内部)联接将失败。但是,如您所知,将会有推荐,您可以使用left-join而不是inner-join,检查连接结果,如果建议为null,则将click事件写回输入主题(即,你得到一个重试逻辑) - 当然,单个推荐的连续点击可能会出现故障,你可能需要在你的应用程序代码中考虑到这一点。
  • KTable的第二个缺点是,随着时间的推移,它将永远增长并且无限制,因为您将为它添加越来越多的独特建议。因此,您需要通过将<recommendationsId, null>形式的逻辑删除记录发送到推荐主题来实现一些“过期逻辑”,以删除您不再关心的旧建议。
  • 这种方法的优点是,与流 - 流连接相比,总共需要更少的内存/磁盘空间,因为您只需缓冲应用程序中的所有建议(但没有点击)。

如果您使用流媒体加入,并且推荐后7天可能会发生点击,则您的窗口大小必须为7天 - 否则,点击不会与推荐相关联。

  • 这种方法的缺点是,您将需要更多的内存/磁盘,因为您将缓冲应用程序中过去7天的所有点击和所有建议。
  • 优点是订单或处理(即推荐与点击)不再重要(即,您不需要像上面描述的那样实施重试策略)。
  • 此外,旧的建议会自动过时,因此您无需实施特殊的“过期逻辑”。

对于流 - 流加入,保留时间的答案略有不同。它必须至少7天,因为窗口大小是7天。否则,您将删除“运行窗口”的记录。您还可以将保留期设置得更长,以便能够处理“延迟数据”。假设用户在窗口时间范围结束时(推荐的7天时间跨度前5分钟)点击,但点击仅在1小时后报告给您的应用程序。如果您的保留期限为7天作为您的窗口大小,则此迟到的记录将无法再处理(因为建议已被删除)。如果您设置较长的保留期限,例如8天,您仍然可以处理延迟记录。这取决于您的应用程序/语义需要您想要使用的保留时间。

摘要的: 从实现的角度来看,使用流 - 流连接比使用流表连接更简单。但是,预计可以节省内存/磁盘,并且可能会很大,具体取决于您的点击流数据速率。