我注意到Spring Cloud Stream的反应和非反应行为不同。当我使用非反应性方法消息时,将一一读取。这是流侦听器的代码片段:
@StreamListener(Processor.INPUT)
public void receive2(String input) throws InterruptedException {
Thread.sleep(2000);
System.out.println(input.toUpperCase());
}
此代码逐个使用消息。我可以从RabbitMQ管理网站上看到这一点:
重新启动后,应用程序将继续使用从重新启动前完成的位置开始的消息。但是,使用反应式流侦听器服务会立即从队列中获取所有消息。重新启动应用程序后,由于队列为空,它不会继续处理消息。
这是反应式流侦听器的片段:
@StreamListener
public void receive1(@Input(Processor.INPUT) Flux<String> input) {
input
.delayElements(Duration.ofSeconds(2))
.map(String::toUpperCase)
.doOnEach(System.out::println)
.subscribe();
}
我想了解为什么会发生这种情况,并且有可能在处理之前的元素之前停止读取整个队列。是我对Reactor运算符的错误理解还是预期的行为?可以通过某种方式指定背压吗?
答案 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