I'm using Spring Cloud Stream "2021.0.2" with Spring Boot "2.6.7" and having trouble disabling a producer from starting up automatically. Seems to work fine for consumer (checked actuator/bindings endpoint).
spring:
kafka:
bootstrap-servers: ${KAFKA_URL:http://localhost:29092}
cloud:
stream:
function:
definition: mqtt;mqttRoot;kafka
bindings:
mqtt-out-0:
destination: input
producer:
autoStartup: false
mqttRoot-out-0:
destination: input
producer:
autoStartup: false
kafka-in-0:
destination: output
group: mqtt
consumer:
autoStartup: false
concurrency: 1
My use case is I'm trying to connect mqtt -> kafka and kafka -> mqtt using the spring-cloud-stream-kafka-binder and hive mq java reactor client. Alternatively is there another way to delay the initialisation of the producer/consumer beans? I've used an ApplicationRunner to connect to mqtt after startup.
@Configuration
@Log4j2
public class InputConfig {
@Value("${mqtt.subscriptionTopic}")
private String subscriptionTopic;
private final Mqtt3ReactorClient inboundMqttClient;
private final Mqtt3ReactorClient inboundRootMqttClient;
@Autowired
public InputConfig(@Qualifier("inboundMqttClient") Mqtt3ReactorClient inboundMqttClient,
@Qualifier("inboundRootMqttClient") Mqtt3ReactorClient inboundRootMqttClient) {
this.inboundMqttClient = inboundMqttClient;
this.inboundRootMqttClient = inboundRootMqttClient;
}
@Bean
public Supplier<Flux<byte[]>> mqtt() {
return new Input(inboundMqttClient, subscriptionTopic);
}
@Bean
public Supplier<Flux<byte[]>> mqttRoot() {
return new Input(inboundRootMqttClient, subscriptionTopic);
}
}
@Log4j2
public class Input implements Supplier<Flux<byte[]>>, ApplicationRunner {
private final Mqtt3ReactorClient inboundMqttClient;
private final String subscriptionTopic;
public Input(Mqtt3ReactorClient inboundMqttClient, String subscriptionTopic) {
this.inboundMqttClient = inboundMqttClient;
this.subscriptionTopic = subscriptionTopic;
}
@Override
public Flux<byte[]> get() {
FluxWithSingle<Mqtt3Publish, Mqtt3SubAck> subAckAndMatchingPublishes = inboundMqttClient.subscribePublishesWith()
.topicFilter(subscriptionTopic).qos(MqttQos.AT_LEAST_ONCE)
.applySubscribe();
return subAckAndMatchingPublishes
.doOnSingle(subAck -> log.info("Subscribed, " + subAck.getReturnCodes()))
.doOnNext(publish -> log.debug(
"Received publish" + ", topic: " + publish.getTopic() + ", QoS: " + publish.getQos() +
", payload: " + new String(publish.getPayloadAsBytes())))
.map(Mqtt3Publish::getPayloadAsBytes);
}
@Override
public void run(ApplicationArguments args) {
Mono<Mqtt3ConnAck> connAckSingle = inboundMqttClient.connect();
connAckSingle
.doOnSuccess(connAck -> log.info("Connected, " + connAck.getReturnCode()))
.doOnError(throwable -> log.info("Connection failed, " + throwable.getMessage()))
.subscribe();
}
}
@Configuration
@Log4j2
public class OutputConfig {
@Value("${mqtt.publishTopic}")
private String publishTopic;
private final Mqtt3ReactorClient outboundMqttClient;
private final Mqtt3ReactorClient outboundRootMqttClient;
@Autowired
public OutputConfig(@Qualifier("outboundMqttClient") Mqtt3ReactorClient outboundMqttClient,
@Qualifier("outboundRootMqttClient") Mqtt3ReactorClient outboundRootMqttClient) {
this.outboundMqttClient = outboundMqttClient;
this.outboundRootMqttClient = outboundRootMqttClient;
}
@Bean
public Consumer<Flux<Output.GatewayNotification>> kafka() {
return new Output(outboundMqttClient, outboundRootMqttClient, publishTopic);
}
}
@Log4j2
public class Output implements Consumer<Flux<Output.GatewayNotification>>, ApplicationRunner {
private final Mqtt3ReactorClient outboundMqttClient;
private final Mqtt3ReactorClient outboundRootMqttClient;
private final String publishTopic;
public Output(Mqtt3ReactorClient outboundMqttClient,
Mqtt3ReactorClient outboundRootMqttClient,
String publishTopic) {
this.outboundMqttClient = outboundMqttClient;
this.outboundRootMqttClient = outboundRootMqttClient;
this.publishTopic = publishTopic;
}
@Override
public void accept(Flux<Output.GatewayNotification> gatewayNotifications) {
Flux<Mqtt3Publish> messagesToPublish = gatewayNotifications
.map(gatewayNotification -> Mqtt3Publish.builder()
.topic(publishTopic.replace("\\{gateway_address\\}", gatewayNotification.getAddress()))
.qos(MqttQos.AT_LEAST_ONCE)
.payload(gatewayNotification.getPayload().getBytes())
.build());
outboundMqttClient.publish(messagesToPublish)
.doOnNext(publishResult -> log.debug(
"Publish acknowledged: " + new String(publishResult.getPublish().getPayloadAsBytes())))
.subscribe();
outboundRootMqttClient.publish(messagesToPublish)
.doOnNext(publishResult -> log.debug(
"Publish acknowledged: " + new String(publishResult.getPublish().getPayloadAsBytes())))
.subscribe();
}
@Override
public void run(ApplicationArguments args) {
Mono<Mqtt3ConnAck> connAckSingle = outboundMqttClient.connect();
connAckSingle
.doOnSuccess(connAck -> log.info("Connected, " + connAck.getReturnCode()))
.doOnError(throwable -> log.info("Connection failed, " + throwable.getMessage()))
.subscribe();
Mono<Mqtt3ConnAck> connAckSingleRoot = outboundRootMqttClient.connect();
connAckSingleRoot
.doOnSuccess(connAck -> log.info("Connected, " + connAck.getReturnCode()))
.doOnError(throwable -> log.info("Connection failed, " + throwable.getMessage()))
.subscribe();
}
@Data
public static class GatewayNotification {
private String address;
private String payload;
private Long buildingId;
}
}
The underlying exception I'm trying to solve is: