我有一个使用Boot 2.0和webflux的应用程序,并且有一个端点返回ServerSentEvent
的Flux。通过利用spring-amqp
消耗RabbitMQ队列中的消息来创建事件。我的问题是:如何最好地将MessageListener
配置的侦听器方法桥接到可以传递给我的控制器的Flux?
Project Reactor的create部分提到它可以非常有用于将现有API与反应世界联系起来 - 例如基于侦听器的异步API",但我和#39; m不确定如何直接挂钩到消息监听器,因为它包含在DirectMessageListenerContainer
和MessageListenerAdapter
中。他们的例子来自创建部分:
Flux<String> bridge = Flux.create(sink -> {
myEventProcessor.register(
new MyEventListener<String>() {
public void onDataChunk(List<String> chunk) {
for(String s : chunk) {
sink.next(s);
}
}
public void processComplete() {
sink.complete();
}
});
});
到目前为止,我最好的选择是创建一个Processor
,每次只需在RabbitMQ监听器方法中调用onNext()
来手动生成一个事件。
答案 0 :(得分:6)
我有这样的事情:
@SpringBootApplication
@RestController
public class AmqpToWebfluxApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(AmqpToWebfluxApplication.class, args);
RabbitTemplate rabbitTemplate = applicationContext.getBean(RabbitTemplate.class);
for (int i = 0; i < 100; i++) {
rabbitTemplate.convertAndSend("foo", "event-" + i);
}
}
private TopicProcessor<String> sseFluxProcessor = TopicProcessor.share("sseFromAmqp", Queues.SMALL_BUFFER_SIZE);
@GetMapping(value = "/sseFromAmqp", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getSeeFromAmqp() {
return this.sseFluxProcessor;
}
@RabbitListener(id = "fooListener", queues = "foo")
public void handleAmqpMessages(String message) {
this.sseFluxProcessor.onNext(message);
}
}
TopicProcessor.share()
允许我们通过WebFlux将TopicProcessor
Flux
作为/sseFromAmqp
作为@RabbitListener
REST请求返回时获得许多并发订阅者。
TopicProcessor
只是将收到的消息委托给main()
。
在TopicProcessor
我有一个代码,即使没有订阅者,也可以确认我可以发布到curl
。
使用RabbitMQ Management Plugin对两个单独的share()
会话进行测试并将消息发布到队列中。
顺便说一下,我使用@RabbitListener
因为:https://projectreactor.io/docs/core/release/reference/#_topicprocessor
在共享配置中创建时,来自多个上游发布者
那是'因为ListenerContainer
实际上可以同时从不同的Sandbox
线程调用。
<强>更新强>
此外,我将此示例移至我的select category, count(*) as categ_count, avg(price) as avg_price
from table
group by category;
:https://github.com/artembilan/sendbox/tree/master/amqp-to-webflux
答案 1 :(得分:3)
假设你想要一个RabbitMQ监听器以某种方式将消息放到一个或多个Flux
(es)。 Flux.create
确实是创建此类Flux
的好方法。
让我们从Messaging with RabbitMQ Spring指南开始,尝试对其进行调整。
原始Receiver
必须进行修改才能将收到的邮件发送到FluxSink
。
@Component
public class Receiver {
/**
* Collection of sinks enables more than one subscriber.
* Have to keep in mind that the FluxSink instance that the emitter works with, is provided per-subscriber.
*/
private final List<FluxSink<String>> sinks = new ArrayList<>();
/**
* Adds a sink to the collection. From now on, new messages will be put to the sink.
* Method will be called when a new Flux is created by calling Flux.create method.
*/
public void addSink(FluxSink<String> sink) {
sinks.add(sink);
}
public void receiveMessage(String message) {
sinks.forEach(sink -> {
if (!sink.isCancelled()) {
sink.next(message);
} else {
// If canceled, don't put any new messages to the sink.
// Sink is canceled when a subscriber cancels the subscription.
sinks.remove(sink);
}
});
}
}
现在我们有一个接收器将RabbitMQ消息放入接收器。然后,创建Flux
非常简单。
@Component
public class FluxFactory {
private final Receiver receiver;
public FluxFactory(Receiver receiver) { this.receiver = receiver; }
public Flux<String> createFlux() {
return Flux.create(receiver::addSink);
}
}
Receiver
bean自动连接到工厂。当然,你不必创建一个特殊的工厂。这仅表明了如何使用Receiver
创建Flux
。
Messaging with RabbitMQ指南中的其余应用程序可能保持不变,包括bean实例化。
@SpringBootApplication
public class Application {
...
@Bean
SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(queueName);
container.setMessageListener(listenerAdapter);
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
...
}
我使用类似的设计来适应Twitter流媒体API。但是,如何做到这一点可能会有更好的方式。