Apache kafka 如何忽略Kafka Streams应用程序中读取和写入同一主题中不同事件类型的某些消息
让我们假设一个Spring Cloud Stream应用程序从一个Apache kafka 如何忽略Kafka Streams应用程序中读取和写入同一主题中不同事件类型的某些消息,apache-kafka,spring-cloud,avro,apache-kafka-streams,spring-cloud-stream,Apache Kafka,Spring Cloud,Avro,Apache Kafka Streams,Spring Cloud Stream,让我们假设一个Spring Cloud Stream应用程序从一个订单主题创建一个KStream。它感兴趣的是OrderCreated{“id”:x,“productId”:y,“customerId”:z}事件。一旦有人到达,它将对其进行处理并生成输出事件OrderShipped{“id”:x,“productId”:y,“customerName”:您可以使用Kafka的记录头来存储记录的类型。请参阅。您可以在中设置头 处理过程如下: 从主题中读取值为serdeSerdes.BytesSer
订单主题创建一个KStream
。它感兴趣的是OrderCreated{“id”:x,“productId”:y,“customerId”:z}
事件。一旦有人到达,它将对其进行处理并生成输出事件OrderShipped{“id”:x,“productId”:y,“customerName”:您可以使用Kafka的记录头来存储记录的类型。请参阅。您可以在中设置头
处理过程如下:
从主题中读取值为serdeSerdes.BytesSerde
的类型为KStream
的流
用于筛选和创建对象。更具体地说,在transformValues()
中,您可以访问包含记录类型信息的记录标题。然后:
- 如果类型为
OrderShipped
,则返回null
- 否则,从
Bytes
对象创建一个OrderCreated
对象并返回它
对于使用AVRO的解决方案,您可能需要查看以下文档
我接受了布鲁诺的答案,认为这是解决这个问题的有效方法。不过,我认为我已经想出了一种更直接/更合理的方法,使用带有JsonTypeInfo
注释的事件层次结构
首先,您需要一个Order事件的基类,并指定所有子类。请注意,JSON文档中将添加一个type属性,这将有助于Jackson封送/解封DTO:
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(value = [
JsonSubTypes.Type(value = OrderCreatedEvent::class, name = "orderCreated"),
JsonSubTypes.Type(value = OrderShippedEvent::class, name = "orderShipped")
])
abstract class OrderEvent
data class OrderCreatedEvent(var id: Int?, var productId: Int?, var customerId: Int?) : OrderEvent() {
constructor() : this(null, null, null)
}
data class OrderShippedEvent(var id: Int?, var productId: Int?, var customerName: String?, var customerAddress: String?) : OrderEvent () {
constructor() : this(null, null, null, null)
}
有了它,OrderCreatedEvent对象的生产者将生成如下消息:
interface ShippingKStreamProcessor {
...
@Input("order")
fun order(): KStream<String, OrderCreated>
@Output("output")
fun output(): KStream<String, OrderShipped>
key:1值:{“type”:“orderCreated”,“id”:1,“productId”:24,“customerId”:1}
现在轮到KStream了。我已经将签名更改为KStream
,因为它可以接收OrderCreatedEvent或OrderShippedEvent。在接下来的两行中
orderEvent.filter { _, value -> value is OrderCreatedEvent }
.map { key, value -> KeyValue(key, value as OrderCreatedEvent) }
…我筛选以仅保留OrderCreatedEvent类的消息,并将它们映射为将KStream
转换为KStream
完整的KStream逻辑:
@StreamListener
@SendTo("output")
fun process(@Input("input") input: KStream<Int, Customer>, @Input("order") orderEvent: KStream<Int, OrderEvent>): KStream<Int, OrderShippedEvent> {
val intSerde = Serdes.IntegerSerde()
val customerSerde = JsonSerde<Customer>(Customer::class.java)
val orderCreatedSerde = JsonSerde<OrderCreatedEvent>(OrderCreatedEvent::class.java)
val stateStore: Materialized<Int, Customer, KeyValueStore<Bytes, ByteArray>> =
Materialized.`as`<Int, Customer, KeyValueStore<Bytes, ByteArray>>("customer-store")
.withKeySerde(intSerde)
.withValueSerde(customerSerde)
val customerTable: KTable<Int, Customer> = input.groupByKey(Serialized.with(intSerde, customerSerde))
.reduce({ _, y -> y }, stateStore)
return (orderEvent.filter { _, value -> value is OrderCreatedEvent }
.map { key, value -> KeyValue(key, value as OrderCreatedEvent) }
.selectKey { _, value -> value.customerId } as KStream<Int, OrderCreatedEvent>)
.join(customerTable, { orderIt, customer ->
OrderShippedEvent(orderIt.id, orderIt.productId, customer.name, customer.address)
}, Joined.with(intSerde, orderCreatedSerde, customerSerde))
.selectKey { _, value -> value.id }
//.to("order", Produced.with(intSerde, orderShippedSerde))
}
@StreamListener
@发送到(“输出”)
趣味流程(@Input(“Input”)Input:KStream,@Input(“order”)orderEvent:KStream):KStream{
val intSerde=Serdes.IntegerSerde()
val customerSerde=JsonSerde(Customer::class.java)
val orderCreatedSerde=JsonSerde(OrderCreatedEvent::class.java)
val stateStore:具体化=
具体化。`as`(“客户商店”)
.withKeySerde(intSerde)
.withValueSerde(customerSerde)
val customerTable:KTable=input.groupByKey(序列化.with(intSerde,customerSerde))
.reduce({uu,y->y},stateStore)
返回(orderEvent.filter{},值->值为OrderCreatedEvent}
.map{key,value->KeyValue(key,值为OrderCreatedEvent)}
.selectKey{{,value->value.customerId}作为KStream)
.join(customerTable,{orderIt,customer->
OrderShippedEvent(orderIt.id、orderIt.productId、customer.name、customer.address)
},加入。使用(intSerde、orderCreatedSerde、customerSerde))
.selectKey{{,value->value.id}
//.to(“订单”,由(intSerde、orderShippedSerde)生成)
}
在此过程之后,我将在订单主题中生成一条新消息key:1值:{“type”:“orderShipped”,“id”:1,“productId”:24,“customerName”:“Anna”,“customerAddress”:“Cipress Street”}
,但这将被流过滤掉。为什么KStream.filter()
对你不起作用吗?因为所有内容都在卡夫卡主题中,那些订单发货
仍将提供给该主题的其他消费者。正如@ArtemBilan提到的,这应该是可以通过过滤器控制的。如果你能分享更多代码,我们可以看看。我已经更新了问题的更多细节我认为你的解决方案可以很好地解决问题,但我对此表示怀疑(ValueTransformerSupplier状态存储可以省略。如果所有事件类型都已知,这对我来说很好。但是,有没有关于如何处理/忽略未知类型事件的建议?例如,可能有一个OrderDeletedEvent
,消费者服务应该忽略它,因为它不相关。在这种情况下,我不想包括取消附加的、不必要的OrderDeletedEvent
类。一种解决方案是记录错误并通过配置org.apache.kafka.streams.errors.LogAndContinueExceptionHandler
继续处理,但这意味着忽略所有可能不需要的反序列化异常。
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(value = [
JsonSubTypes.Type(value = OrderCreatedEvent::class, name = "orderCreated"),
JsonSubTypes.Type(value = OrderShippedEvent::class, name = "orderShipped")
])
abstract class OrderEvent
data class OrderCreatedEvent(var id: Int?, var productId: Int?, var customerId: Int?) : OrderEvent() {
constructor() : this(null, null, null)
}
data class OrderShippedEvent(var id: Int?, var productId: Int?, var customerName: String?, var customerAddress: String?) : OrderEvent () {
constructor() : this(null, null, null, null)
}
orderEvent.filter { _, value -> value is OrderCreatedEvent }
.map { key, value -> KeyValue(key, value as OrderCreatedEvent) }
@StreamListener
@SendTo("output")
fun process(@Input("input") input: KStream<Int, Customer>, @Input("order") orderEvent: KStream<Int, OrderEvent>): KStream<Int, OrderShippedEvent> {
val intSerde = Serdes.IntegerSerde()
val customerSerde = JsonSerde<Customer>(Customer::class.java)
val orderCreatedSerde = JsonSerde<OrderCreatedEvent>(OrderCreatedEvent::class.java)
val stateStore: Materialized<Int, Customer, KeyValueStore<Bytes, ByteArray>> =
Materialized.`as`<Int, Customer, KeyValueStore<Bytes, ByteArray>>("customer-store")
.withKeySerde(intSerde)
.withValueSerde(customerSerde)
val customerTable: KTable<Int, Customer> = input.groupByKey(Serialized.with(intSerde, customerSerde))
.reduce({ _, y -> y }, stateStore)
return (orderEvent.filter { _, value -> value is OrderCreatedEvent }
.map { key, value -> KeyValue(key, value as OrderCreatedEvent) }
.selectKey { _, value -> value.customerId } as KStream<Int, OrderCreatedEvent>)
.join(customerTable, { orderIt, customer ->
OrderShippedEvent(orderIt.id, orderIt.productId, customer.name, customer.address)
}, Joined.with(intSerde, orderCreatedSerde, customerSerde))
.selectKey { _, value -> value.id }
//.to("order", Produced.with(intSerde, orderShippedSerde))
}