Я заметил разницу в реактивном и безреактивном поведении Spring Cloud Stream. Когда я использую нереактивный подход, сообщения читаются одно за другим. Вот фрагмент кода для прослушивателя потока:

@StreamListener(Processor.INPUT)
public void receive2(String input) throws InterruptedException {
    Thread.sleep(2000);
    System.out.println(input.toUpperCase());
}

Этот код потребляет сообщения одно за другим. Я вижу это на сайте RabbitMQ Management:

enter image description here

После перезапуска приложение продолжает получать сообщения с того места, где оно было завершено, до перезапуска. Но при использовании службы прослушивателя реактивного потока все сообщения сразу же извлекаются из очереди. После перезапуска приложение не продолжает обработку сообщений, поскольку очередь пуста.

А вот прослушиватель реактивного потока обрезан:

@StreamListener
public void receive1(@Input(Processor.INPUT) Flux<String> input) {
    input
            .delayElements(Duration.ofSeconds(2))
            .map(String::toUpperCase)
            .doOnEach(System.out::println)
            .subscribe();
}

enter image description here

Я хочу понять, почему это происходит и можно ли прекратить чтение всей очереди перед обработкой предыдущих элементов. Это мое неправильное понимание операторов Reactor или это ожидаемое поведение? Можно ли как-нибудь указать противодавление?

2
solomkinmv 8 Окт 2018 в 17:45

2 ответа

Лучший ответ

Поскольку реактивный - это неблокирующий; delayElements() передает работу другому потоку; это освобождает поток слушателя, который возвращается в контейнер и подтверждает сообщение; следовательно, следующий доставлен (и отложен); Итак, все сообщения попадают в очередь планировщика. Reactive действительно не подходит для этого варианта использования, если вы не используете ручные подтверждения, как показано ниже.

@StreamListener
public void receive1(@Input(Processor.INPUT) Flux<Message<String>> input) {
    input
            .doOnEach(System.out::println)
            .delayElements(Duration.ofSeconds(2))
            .map(m -> {
                return MessageBuilder.withPayload(m.getPayload().toUpperCase())
                        .copyHeaders(m.getHeaders())
                        .build();
            })
            .doOnEach(System.out::println)
            .doOnNext(m -> {
                try {
                    m.getHeaders().get(AmqpHeaders.CHANNEL, Channel.class)
                        .basicAck(m.getHeaders().get(AmqpHeaders.DELIVERY_TAG, Long.class), false);
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            })
            .subscribe();
}

И

spring.cloud.stream.bindings.input.group=foo
spring.cloud.stream.rabbit.bindings.input.consumer.acknowledge-mode=manual
1
Gary Russell 8 Окт 2018 в 15:55

Вероятно, нам нужно посмотреть, как подключены реактивные соединения, поскольку это действительно кажется странным. Сказав, что если вы используете последнюю версию spring-cloud-stream, я бы порекомендовал немного изменить ваше приложение и полагаться на поддержку Spring Cloud Function, которая для вашего случая по существу обеспечивает альтернативную поддержку для реализации реактивных обработчиков сообщений - https: // spring .io / блог / 2018 / 08 / 28 / весна - облако - поток - fishtown - m2-2-1-0 - m2- релиз - анонс. Единственное изменение, которое вы должны внести в пример в блоге:

@Bean
public Consumer<Flux<String>> foo() {
    //
}
1
Oleg Zhurakousky 8 Окт 2018 в 15:05