在spring-webflux

时间:2018-04-04 23:58:13

标签: spring-amqp project-reactor

我有一个使用Boot 2.0和webflux的应用程序,并且有一个端点返回ServerSentEvent的Flux。通过利用spring-amqp消耗RabbitMQ队列中的消息来创建事件。我的问题是:如何最好地将MessageListener配置的侦听器方法桥接到可以传递给我的控制器的Flux?

Project Reactor的create部分提到它可以非常有用于将现有API与反应世界联系起来 - 例如基于侦听器的异步API",但我和#39; m不确定如何直接挂钩到消息监听器,因为它包含在DirectMessageListenerContainerMessageListenerAdapter中。他们的例子来自创建部分:

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()来手动生成一个事件。

2 个答案:

答案 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。但是,如何做到这一点可能会有更好的方式。