Spring Kafka Stream未编写

时间:2019-05-03 09:07:03

标签: spring spring-boot apache-kafka spring-cloud-stream

我正在编写一个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创建它,即sensorTablesensorDataStream来自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的瘦委托包装吗?

1 个答案:

答案 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的类的数据类,这将使它更容易理解。