我正在编写一个Spring Boot(2.1.4)应用程序,试图将Spring Cloud Streams用于Kafka。
我想做的是维护一个主题上的传感器列表(“传感器”)。 OTOH,我有另一个主题的传入数据(“数据”)。我要实现的目标是,当我获取尚无的传感器数据时,我想将其添加到传感器列表中。
为此,我从传感器主题创建一个KTable<String, Sensor>
,将温度主题映射到纯传感器的数据(在本例中为其名称),并使用一个ValueJoiner
进行外部联接保留传感器(如果存在),否则使用读数的传感器。然后,将结果写回传感器主题。
KTable<String, Sensor> sensorTable = ...;
KStream<String, SensorData> sensorDataStream = ...;
// get sensors providing measurements
KTable<String, Sensor> sensorsFromData =
sensorDataStream.groupByKey()
.aggregate(
Sensor::new,
(k, v, s) -> {
s.setName(k);
return s;
},
Materialized.with(Serdes.String(), SensorSerde.SERDE));
// join both sensor tables, preferring the existing ones
KTable<String, Sensor> joinedSensorTable =
sensorTable.outerJoin(
sensorsFromData,
// only use sensors from measurements if sensor not already present
(ex, ft) -> (ex != null) ? ex : ft,
Materialized.<String, Sensor, KeyValueStore<Bytes, byte[]>>as(SENSORS_TABLE)
.withKeySerde(Serdes.String()).withValueSerde(SensorSerde.SERDE));
// write to new topic for downstream services
joinedSensorTable.toStream();
如果我使用StreamBuilder
创建它,即sensorTable
和sensorDataStream
来自builder.table("sensors", Consumed.with(Serdes.String(), SensorSerde.SERDE))
之类的东西,这会很好。
但是,我试图为此使用Spring Stream Binding,即上面的代码被包裹在
@Configuration
@EnableBinding(SensorTableBinding.class)
class StreamConfiguration {
static final String SENSORS_TABLE = "sensors-table";
@StreamListener
@SendTo("sensorsOut")
private KStream<String, Sensor> getDataFromData
(@Input("sensors") KTable<String, Sensor> sensorTable,
@Input("data") KStream<String, SensorData> sensorDataStream) {
// ...
return joinedSensorTable.toStream();
}
}
带有
interface SensorTableBinding {
@Input("sensors")
KTable<String, Sensor> sensorStream();
@Output("sensorsOut")
KStream<String, Sensor> sensorOutput();
@Input("data")
KStream<String, SensorData> sensorDataStream();
}
这是application.properties的spring stream部分:
spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde: org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde: org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.kafka.binder.brokers: ${spring.kafka.bootstrap-servers}
spring.cloud.stream.kafka.binder.configuration.auto.offset.reset: latest
spring.cloud.stream.kafka.binder.bindings.sensors.group: sensor-service
spring.cloud.stream.kafka.binder.bindings.sensors.destination: sensors
spring.cloud.stream.kafka.binder.bindings.sensorsOut.destination: sensors
spring.cloud.stream.kafka.binder.data.group: sensor-service
spring.cloud.stream.kafka.binder.data.destination: data
可以很好地初始化流,并执行连接(正确填充键值存储),但是,结果流永远不会写入“ sensors”主题。
为什么?我想念什么吗?
另外:我敢肯定,有一种更好的方法可以使用现有的Serde将对象从JSON解序列化到JSON,而不必声明自己的类来添加到处理中(SensorSerde
/ { {1}}是SensorDataSerde
的瘦委托包装吗?
答案 0 :(得分:0)
证明数据毕竟是写的,但是是错误的主题,即sensorOut
。
原因是配置。代替
spring.cloud.stream.kafka.binder.bindings.sensors.destination: sensors
spring.cloud.stream.kafka.binder.bindings.sensorsOut.destination: sensors
主题配置如下:
spring.cloud.stream.bindings.sensors.destination: sensors
spring.cloud.stream.bindings.sensorsOut.destination: sensors
对于传感器和数据主题,这无关紧要,因为绑定的名称与该主题相同。但是由于Spring找不到输出的正确目的地,因此它使用了绑定的名称sensorOut
并将数据写入那里。
请注意,围绕这些的整个配置设置非常令人困惑。各个项目都有文档记录,但是很难区分每个项目所属的配置前缀。查看源代码也无济于事,因为在该级别传递的是Map
,它们在运行时除去了前缀,因此很难分辨数据来自何处以及来自何处。将包含。
IMO,这将真正有助于传递类似@ConfigurationProperties
的类的数据类,这将使它更容易理解。