Kafka流-KSQL-拆分消息并发布到另一个主题

时间:2019-12-03 02:26:50

标签: apache-kafka ksql ksqldb

是否有一种方法可以使用KSQL将消息拆分为多条消息并发布到新主题。需要明确的是,我不是在寻找基于Java的侦听器并将其迭代/流式传输到一个新主题。相反,我正在寻找可以为我做的KSQL。

例如:

比方说,我需要将invoice主题中的消息分成item_inventory_delta条消息

发票主题

:saleschecknumber

消息示例:

{
    "total": 12.33,
    "salecounter": 1,
    "items": [
        {
            "itemId": 123,
            "quantity": 1
        },
        {
            "itemId": 345,
            "quantity": 5
        }
    ]
}

item_inventory_delta 主题

:saleschecknumber_itemID

消息示例

1。

{
    "itemId": 123,
    "quantity": 1
}

2。

{
    "itemId": 345,
    "quantity": 5
}

3 个答案:

答案 0 :(得分:2)

由于the addition中的EXPLODE table function,从ksqlDB 0.6开始,您现在可以执行此操作。

根据您的示例给出一个带有JSON负载的主题invoice,请首先使用PRINT对该主题进行检查以转储其内容:

ksql> PRINT invoice FROM BEGINNING;
Format:JSON
{"ROWTIME":1575366231505,"ROWKEY":"null","total":12.33,"salecounter":1,"items":[{"itemId":123,"quantity":1},{"itemId":345,"quantity":5}]}

然后在该主题的主题上声明一个模式,这为我们提供了一个ksqlDB

CREATE STREAM INVOICE (total DOUBLE, 
                       salecounter INT, 
                       items ARRAY<STRUCT<itemId INT, 
                                          quantity INT>>) 
                WITH (KAFKA_TOPIC='invoice', 
                      VALUE_FORMAT='JSON');

这只是“注册”现有主题以与ksqlDB一起使用。直到下一步,才编写新的Kafka主题。

创建一个新的Kafka主题,从源流中收到的消息中不断填充该主题:

CREATE STREAM INVENTORY WITH (KAFKA_TOPIC='item_inventory_delta') AS 
  SELECT EXPLODE(ITEMS)->ITEMID AS ITEMID, 
         EXPLODE(ITEMS)->QUANTITY AS QUANTITY 
    FROM INVOICE;

已创建新主题:

ksql> SHOW TOPICS;

 Kafka Topic                     | Partitions | Partition Replicas
-------------------------------------------------------------------
 invoice                         | 1          | 1
 item_inventory_delta            | 1          | 1

主题具有请求的增量消息:)

ksql> PRINT item_inventory_delta;
Format:JSON
{"ROWTIME":1575366231505,"ROWKEY":"null","ITEMID":123,"QUANTITY":1}
{"ROWTIME":1575366231505,"ROWKEY":"null","ITEMID":345,"QUANTITY":5}

答案 1 :(得分:1)

有许多方法可以使我理解,这与我们如何处理传入消息而不汇总消息有关。使用Kafka流处理器API的简单方法,可让您自定义处理逻辑。

Kafka Stream Processor API

  

Processor API允许开发人员定义和连接自定义   处理器并与状态存储进行交互。使用Processor API,   您可以定义处理一个接收到的任意流处理器   一次记录,并将这些处理器与其关联的   状态存储以构成代表一个   定制处理逻辑

注意 :您尚未定义将输出值的内容,因此我只是发布键和值相同,但是您可以选择定义输出键,值

您可以如下定义Kafka Stream处理器API

Topology builder = new Topology();
builder.addSource("Source", "invoice")
                .addProcessor("sourceProcessor", () -> new InvoiceProcessor(), "Source")
                .addSink("sinkDeltaInvoice", "item_inventory_delta", Serdes.String().serializer(), Serdes.String().serializer(),
                        "sourceProcessor")

以下是自定义处理器方法,请注意其公正方法尚未完全实施

class InvoiceProcessor implements Processor<String, String> {
        private Gson gson = new Gson();

        //constructor
        .......
        private ProcessorContext context;

        @Override
        public void init(ProcessorContext context) {
            this.context = context;

        }

        @Override
        public void close() {
            // Any code for clean up would go here. This processor instance will not be used
            // again after this call.
        }

        @Override
        public void process(String key, String value) {
            try {

                //Create custom inventory to map JSON object  
                //List[Item] items is member object of Inventory class
                Inventory inventory = gson.fromJson(key, Inventory.class);


                //itertae item of items List[Items]
                for(Item item: inventory.getItems()){
                context.forward(gson.toJson(item), gson.toJson(item), To.child("sinkDeltaInvoice"));

                }
                //


                }


        }

    }  

答案 2 :(得分:0)

对于KStream应用程序,您可以使用flatMap,该函数接受接受一条记录并返回零个或多个记录的可迭代项的功能:

case class Record(total: Double, salecounter: Int, items: List[Item])
case class Item(itemId: Int, quantity: Int)

// initialize the stream 
val inputStream: KStream[String, Record] = ??? 

// split the message
inputStream.flatMap { case (key, record) => 
  record.items.map(item => (key, item) )
}