0

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:

enter image description here

Rod McCutcheon
  • 201
  • 4
  • 21
  • I wonder if the reactive supplier does not honor the `autoStartup` property. Can you create two small Boot applications, one imperative and another reactive (without involving MQTT, but just plain suppliers)? Then set the property on the producer binding and see if you can reproduce the issue. We can triage further based on your findings. – sobychacko Jun 23 '22 at 15:43
  • Thanks @sobychacko, is it related to this issue? https://github.com/spring-cloud/spring-cloud-stream/issues/2212 – Rod McCutcheon Jun 24 '22 at 01:35
  • I am not sure it is but will have to look into it. Did you get a chance to verify using a simple imperative and reactive app? – sobychacko Jun 24 '22 at 18:35

0 Answers0