简体   繁体   English

使用Spring Boot RabbitMQ的异步RPC

[英]Asynchronous RPC using Spring Boot RabbitMQ

I have implemented a basic asynchronous RPC call using spring boot 1.4 and rabbit mq. 我已经使用Spring Boot 1.4和Rabbit MQ实现了一个基本的异步RPC调用。
My intention is to use this example as a basis of communication among micro services. 我的意图是将此示例用作微服务之间通信的基础。
For example, Publisher.java and Subscriber.java could be two micro services talking to each other. 例如,Publisher.java和Subscriber.java可以是两个相互通信的微服务。

The code shown works fine, but I am curious to know if there are any better ways of doing this? 显示的代码可以正常工作,但是我很好奇是否有更好的方法可以做到这一点?

My queries as follows: 我的查询如下:

  • For subscriber to listen to request queue using @RabbitListener annotation , I did not had to declare directExchange() and binding() beans in configuration. 为了使订户能够使用@RabbitListener批注来侦听请求队列,我不必在配置中声明directExchange()binding() Bean。
    But for asyncRabbitTemplate to read response from reply queue, I had to declare directExchange() and binding() beans in configuration. 但是, asyncRabbitTemplate从回复队列读取响应,我必须在配置中声明directExchange()binding() Bean。
    Is there any way I can avoid it, because I feel it is code duplication as I am declaring these beans twice. 有什么办法可以避免它,因为我感觉这是代码重复,因为我两次声明了这些bean。
  • In real world application, there would be many such calls between micro services.And as per my understanding , I would need to declare similar rpcReplyMessageListenerContainer() and asyncRabbitTemplate() for each request-reply call. 在现实世界的应用程序中,微服务之间会有很多这样的调用。据我的理解,我需要为每个请求-响应调用声明类似的rpcReplyMessageListenerContainer()asyncRabbitTemplate()
    Is that correct? 那是对的吗?

Code as follows. 代码如下。 Link to Github 链接到Github

Config.java Config.java

@Configuration("asyncRPCConfig")
@Profile("async_rpc")
@EnableScheduling
@EnableRabbit
@ComponentScan(basePackages = {"in.rabbitmq.async_rpc"})
public class Config {

    @Value("${queue.reply}")
    private String replyQueue;
    @Value("${exchange.direct}")
    private String directExchange;
    @Value("${routingKey.reply}")
    private String replyRoutingKey;

    @Bean
    public Publisher publisher() {
        return new Publisher();
    }

    @Bean
    public SimpleRabbitListenerContainerFactory simpleMessageListenerContainerFactory(ConnectionFactory connectionFactory,
                                                                                      SimpleRabbitListenerContainerFactoryConfigurer configurer) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();

        configurer.configure(factory, connectionFactory);
        return factory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        template.setMessageConverter(jsonMessageConverter());
        return template;
    }

    @Bean
    public MessageConverter jsonMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    @Bean
    public Queue replyQueueRPC() {
        return new Queue(replyQueue);
    }

    @Bean
    public SimpleMessageListenerContainer rpcReplyMessageListenerContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer(connectionFactory);
        simpleMessageListenerContainer.setQueues(replyQueueRPC());
        simpleMessageListenerContainer.setReceiveTimeout(2000);
        simpleMessageListenerContainer.setTaskExecutor(Executors.newCachedThreadPool());
        return simpleMessageListenerContainer;
    }


    @Bean
    public AsyncRabbitTemplate asyncRabbitTemplate(ConnectionFactory connectionFactory) {

        return new AsyncRabbitTemplate(rabbitTemplate(connectionFactory),
                        rpcReplyMessageListenerContainer(connectionFactory),
                        directExchange + "/" + replyRoutingKey);
    }

    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange(directExchange);
    }

    @Bean
    public Binding binding() {
        return BindingBuilder.bind(replyQueueRPC()).to(directExchange()).with(replyRoutingKey);
    }


    @Bean
    public Subscriber subscriber() {
        return new Subscriber();
    }

}

Publisher.java Publisher.java

public class Publisher {

    @Value("${routingKey.request}")
    private String requestRoutingKey;
    @Autowired
    private DirectExchange directExchange;

    private static SecureRandom SECURE_RANDOM;

    static {
        try {
            SECURE_RANDOM = SecureRandom.getInstanceStrong();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }


    @Autowired
    private AsyncRabbitTemplate asyncRabbitTemplate;


    @Scheduled(fixedDelay = 100 * 1)
    public void publishToDirectExchangeRPCStyle() {
        Integer integer = SECURE_RANDOM.nextInt();
        SampleRequestMessage sampleRequestMessage = new SampleRequestMessage(String.valueOf(integer));
        System.out.println("Sending out message on direct directExchange:" + sampleRequestMessage);

        AsyncRabbitTemplate.RabbitConverterFuture<SampleResponseMessage> sampleResponseMessageRabbitConverterFuture = asyncRabbitTemplate
                        .convertSendAndReceive(directExchange.getName(), requestRoutingKey, sampleRequestMessage);
        sampleResponseMessageRabbitConverterFuture.addCallback(
                        sampleResponseMessage ->
                                        System.out.println("Response for request message:" + sampleRequestMessage + " is:" + sampleResponseMessage)
                        , failure ->
                                        System.out.println(failure.getMessage())
        );

    }
}

Subscriber.java Subscriber.java

public class Subscriber {

    @RabbitHandler
    @RabbitListener(
                    bindings = {
                                    @QueueBinding(value = @Queue("${queue.request}"),
                                                    key = "${routingKey.request}",
                                                    exchange = @Exchange(value = "${exchange.direct}", type = ExchangeTypes.DIRECT, durable = "true"))})
    public SampleResponseMessage subscribeToRequestQueue(@Payload SampleRequestMessage sampleRequestMessage, Message message) {
        System.out.println("Received message :" + message);
        return new SampleResponseMessage(sampleRequestMessage.getMessage());
    }
}

Your solution is fine. 您的解决方案很好。

It is not clear what you are asking... 不清楚您在问什么...

I had to declare directExchange() and binding() beans in configuration. 我必须在配置中声明directExchange()和binding()Bean。 Is there any way I can avoid it, because I feel it is code duplication as I am declaring these beans twice. 有什么办法可以避免它,因为我感觉这是代码重复,因为我两次声明了这些bean。

@QueueBinding is simply a convenience on @RabbitListener and an alternative to declaring the queue, exchange and binding as @Bean s. @QueueBinding是简单地在一个方便@RabbitListener并宣布该队列,交换和作为结合的替代@Bean秒。

If you are using a common @Config class you can simply omit the bindings attribute on the listener and use queues = "${queue.reply}" to avoid the duplication. 如果使用通用的@Config类,则可以简单地忽略侦听器上的bindings属性,并使用queues = "${queue.reply}"来避免重复。

I would need to declare similar rpcReplyMessageListenerContainer() and asyncRabbitTemplate() for each request-reply call. 我将需要为每个请求-应答调用声明类似的rpcReplyMessageListenerContainer()和asyncRabbitTemplate()。 Is that correct? 那是对的吗?

Yes; 是; although with the upcoming 2.0 release, you can use a DirectReplyToMessageListenerContainer which avoids the need for a separate reply queue for each service; 尽管在即将发布的2.0版本中,您可以使用DirectReplyToMessageListenerContainer ,从而避免了每个服务都需要单独的回复队列的DirectReplyToMessageListenerContainer when you send a message. 当您发送消息时。

See the documentation here and here . 请在此处此处 查看文档

Starting with version 2.0, the async template now supports Direct reply-to instead of a configured reply queue. 从2.0版开始,异步模板现在支持直接答复而不是配置的答复队列。

(Should read "as an alternative to " rather than "instead of"). (应读为“替代”而不是“代替”)。

So you can use the same template to talk to multiple services. 因此,您可以使用同一模板与多个服务进行通话。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM