响应式Spring Cloud Stream从队列中读取所有数据,但非响应式则逐一读取消息

时间:2018-10-08 14:45:09

标签: spring rabbitmq spring-cloud spring-cloud-stream spring-rabbit

我注意到Spring Cloud Stream的反应和非反应行为不同。当我使用非反应性方法消息时,将一一读取。这是流侦听器的代码片段:

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

此代码逐个使用消息。我可以从RabbitMQ管理网站上看到这一点:

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 个答案:

答案 0 :(得分:1)

我们可能需要看一下反应性如何接线,因为看上去确实很奇怪。话虽如此,如果您使用的是最新的spring-cloud-stream,我建议对您的应用进行一些更改,并依靠Spring Cloud Function支持,这对您而言基本上为实现反应式消息处理程序提供了另一种支持-https://spring.io/blog/2018/08/28/spring-cloud-stream-fishtown-m2-2-1-0-m2-release-announcement 。 您应该对博客中的示例进行的唯一更改是:

@Bean
public Consumer<Flux<String>> foo() {
    //
}

答案 1 :(得分:1)

由于反应性是非阻塞的; 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