如何使用KTable作为参考数据来更新KStream?

时间:2018-07-18 08:58:38

标签: apache-kafka apache-kafka-streams

我有一个Kafka主题,其中包含Json中的数据:

{"id": "A", "country": "France"}
{"id": "B", "currency": "£"}

我想用“引用表”之类的内容来规范化内容:

country ( "France" ) -> "FR"
currency ( "£" ) -> "GBP"

为了输出:

{"id": "A", "country": "FR"}
{"id": "B", "currency": "GBP"}

我认为这是使用KTable存储参考数据的典型用例。但是我对实现有些坚持。

当前状态

提取参考数据

在Kafka上创建的专用主题:poc-mapping-in

Json数据示例提供了主题:

{"mapping":"ccy",     "from":"£",      "to":"GBP"}
{"mapping":"country", "from":"France", "to":"FR"}

对键和值进行重新处理后,在KTable中提取的数据:

         KStream<String, String> mappingStream = builder
                .stream("poc-mapping-in",consumed)
                .map(
                     (key, value) -> KeyValue.pair(
                         value.get("mapping")+"#"+value.get("from"), 
                         value.get("to").asText())
         );

         KGroupedStream<String, String> mappingGroupedStream = mappingStream.groupByKey(
                 Serialized.with(Serdes.String(),Serdes.String() ));


         KTable<String,String> mappingTable = mappingGroupedStream.aggregate(
                () -> "", //initializer 
                (aggKey, newValue, aggValue) -> newValue, // adder 
                Materialized.<String, String, KeyValueStore<Bytes, byte[]>>as("ReferenceStore")
                    .withValueSerde(Serdes.String())
                    .withKeySerde(Serdes.String())
                );

           // Testing
           mappingTable.toStream().to("poc-mapping-in-content", 
                Produced.with(Serdes.String(), Serdes.String()));

在主题poc-mapping-in-content中,我得到了以下几行:

"currency"#"£"      GBP
"country"#"France"  FR

这看起来像我期望的那样。双引号很奇怪,但这并不能阻止我进一步前进。

数据已经/应该存储在名为ReferenceStore的本地存储中。

摄取业务流

在Kafka上创建的主题:poc-raw-events

Json数据示例提供了主题:

{"id": "A", "country": "France"}
{"id": "B", "currency": "£"}

提取到KStream中的数据:

  final Consumed<String, JsonNode> consumed = Consumed.with(Serdes.String(), jsonSerde);
  KStream<String, JsonNode> businessData = builder.stream("poc-raw-events", consumed);

从这里我不知道该怎么办。从技术上讲,我知道如何更新JsonNode中的属性。因此,我尝试通过以下方式在KStreamforeach之间循环:

    businessData.foreach(new ForeachAction<String, JsonNode>()  {
        public void apply(String k, JsonNode v) {
            System.out.println(k+ " : " +v);
                    if (v==null) {System.out.println("NULL detected"); return;}
            Iterator<Entry<String, JsonNode>> fields = v.fields();
            int i=0;
            while (fields.hasNext()) {
                i++;
                Entry<String, JsonNode> next = fields.next();
                System.out.println(k+ " field #"+i+" : " +next.getKey() + " -- " + next.getValue());

                String key = next.getKey() + "#" + next.getValue());
//              ((ObjectNode) v).put(next.getKey(), "  WHAT HERE ??? ");

            }

        }
    });

我的想法是用参考KTable中存在的数据替换最后一行中的" WHAT HERE ??? "。但是如何???

  • 我在KTable上找不到类似.findByKey()的东西。
  • 我不知道如何访问ReferenceStore本地存储,因为访问它的方式类似于myKafkaStream.store(...),目前myKafkaStream尚未启动,也没有启动甚至建成。

我想到的另一种方法是使用KStream leftJoin KTable功能。但是我读过某个地方(我没有收藏……),为此,我们应该在两个KTables中使用相同的键。但就我而言,在Json方面,我不是在联接键上工作,而是在一个简单的属性上工作。

您将如何实施?

2 个答案:

答案 0 :(得分:4)

由于您使用的是参考数据,因此我认为您要考虑使用的是GlobalKTable。如上所述,每个GlobalKTable实例都完全复制了一个KafkaStreams,并被显式创建以保存用例的参考数据。

KStream-GlobalKTable连接的独特之处在于您可以使用流的KeyValue来映射到GlobalKTable的键。因此,只要您可以将属性从JsonNode中拉出,就应该能够与GlobalKTable.

中的相应记录一起加入

答案 1 :(得分:0)

如果referenceKTable具有与data.getAltKey()匹配的键

streamToMap.selectKey((originalKey, data) -> data.getAltKey()).leftJoin(referenceKTable, valueJoiner)

可以做到。 valueJoiner(或lambda)的实现必须结合两个输入。