Spring 如何在使用带请求/应答模式的AsyncRabbitTemplate时构建非阻塞使用者

Spring 如何在使用带请求/应答模式的AsyncRabbitTemplate时构建非阻塞使用者,spring,rabbitmq,spring-rabbit,Spring,Rabbitmq,Spring Rabbit,我是rabbitmq的新手,目前正在尝试用非阻塞消费者实现非阻塞生产者。我构建了一些测试生成器,在其中我使用了typereference: 在春季用rabbitmq实现这种行为的最佳方式是什么 编辑配置类: @Configuration @EnableRabbit public class RabbitMQConfig implements RabbitListenerConfigurer { public static final String topicExchangeName

我是rabbitmq的新手,目前正在尝试用非阻塞消费者实现非阻塞生产者。我构建了一些测试生成器,在其中我使用了typereference:

在春季用rabbitmq实现这种行为的最佳方式是什么

编辑配置类:

@Configuration
@EnableRabbit
public class RabbitMQConfig implements RabbitListenerConfigurer {

    public static final String topicExchangeName = "exchange";

    @Bean
    TopicExchange exchange() {
        return new TopicExchange(topicExchangeName);
    }

    @Bean
    public ConnectionFactory rabbitConnectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost("localhost");
        return connectionFactory;
    }

    @Bean
    public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
        return new MappingJackson2MessageConverter();
    }

    @Bean
    public RabbitTemplate rabbitTemplate() {
        final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
        rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
        return rabbitTemplate;
    }

    @Bean
    public AsyncRabbitTemplate asyncRabbitTemplate() {
        return new AsyncRabbitTemplate(rabbitTemplate());
    }

    @Bean
    public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    @Bean
    Queue queue() {
        return new Queue("test", false);
    }

    @Bean
    Binding binding() {
        return BindingBuilder.bind(queue()).to(exchange()).with("foo.#");
    }

    @Bean
    public SimpleRabbitListenerContainerFactory myRabbitListenerContainerFactory() {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(rabbitConnectionFactory());
        factory.setMaxConcurrentConsumers(5);
        factory.setMessageConverter(producerJackson2MessageConverter());
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return factory;
    }

    @Override
    public void configureRabbitListeners(final RabbitListenerEndpointRegistrar registrar) {
        registrar.setContainerFactory(myRabbitListenerContainerFactory());
    }

}

我现在没有时间测试它,但是像这样的东西应该可以工作;您可能不想丢失消息,因此需要将ackMode设置为MANUAL,并自己进行确认(如图所示)

更新

@SpringBootApplication
public class So52173111Application {

    private final ExecutorService exec = Executors.newCachedThreadPool();

    @Autowired
    private RabbitTemplate template;

    @Bean
    public ApplicationRunner runner(AsyncRabbitTemplate asyncTemplate) {
        return args -> {
            RabbitConverterFuture<Object> future = asyncTemplate.convertSendAndReceive("foo", "test");
            future.addCallback(r -> {
                System.out.println("Reply: " + r);
            }, t -> {
                t.printStackTrace();
            });
        };
    }

    @Bean
    public AsyncRabbitTemplate asyncTemplate(RabbitTemplate template) {
        return new AsyncRabbitTemplate(template);
    }

    @RabbitListener(queues = "foo")
    public void listen(String in, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag,
            @Header(AmqpHeaders.CORRELATION_ID) String correlationId,
            @Header(AmqpHeaders.REPLY_TO) String replyTo) {

        ListenableFuture<String> future = handleInput(in);
        future.addCallback(result -> {
            Address address = new Address(replyTo);
            this.template.convertAndSend(address.getExchangeName(), address.getRoutingKey(), result, m -> {
                m.getMessageProperties().setCorrelationId(correlationId);
                return m;
            });
            try {
                channel.basicAck(tag, false);
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }, t -> {
            t.printStackTrace();
        });
    }

    private ListenableFuture<String> handleInput(String in) {
        SettableListenableFuture<String> future = new SettableListenableFuture<String>();
        exec.execute(() -> {
            try {
                Thread.sleep(2000);
            }
            catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            future.set(in.toUpperCase());
        });
        return future;
    }

    public static void main(String[] args) {
        SpringApplication.run(So52173111Application.class, args);
    }

}
@springboot应用程序
公共类SO52173111应用程序{
private final ExecutorService exec=Executors.newCachedThreadPool();
@自动连线
私有rabbit模板;
@豆子
公共应用程序运行程序运行程序(AsyncRabbitTemplate asyncTemplate){
返回参数->{
RabbitConverterFuture=asyncTemplate.convertSendAndReceive(“foo”,“test”);
future.addCallback(r->{
System.out.println(“回复:+r”);
},t->{
t、 printStackTrace();
});
};
}
@豆子
公共异步RabbitTemplate异步模板(RabbitTemplate模板){
返回新的AsyncRabbitTemplate(模板);
}
@RabbitListener(queues=“foo”)
public void listen(字符串输入,通道通道,@Header(AmqpHeaders.DELIVERY_标记)长标记,
@标题(AmqpHeaders.CORRELATION_ID)字符串correlationId,
@标题(AmqpHeaders.REPLY_TO)字符串replyTo){
ListenableFuture=handleInput(in);
future.addCallback(结果->{
地址=新地址(replyTo);
this.template.convertAndSend(address.getExchangeName(),address.getRoutingKey(),result,m->{
m、 getMessageProperties().setCorrelationId(correlationId);
返回m;
});
试一试{
channel.basicAck(标签,假);
}
捕获(IOE异常){
e、 printStackTrace();
}
},t->{
t、 printStackTrace();
});
}
private ListenableFuture handleInput(字符串输入){
SettableListenableFuture=新的SettableListenableFuture();
exec.execute(()->{
试一试{
《睡眠》(2000年);
}
捕捉(中断异常e){
Thread.currentThread().interrupt();
}
future.set(在.toUpperCase()中);
});
回归未来;
}
公共静态void main(字符串[]args){
SpringApplication.run(So52173111Application.class,args);
}
}

谢谢您的快速回答。我已经实现了你的代码,但是我得到了制作人的AMQPrelyTimeOutException。消费者获得消息和确认。我正在使用folling Producer调用:
RabbitConverterFuture test=template.convertSendAndReceiveAsType(“exchange”、“foo.bar”、“hello world”,new parametedtypereference(){})。我将使用config类编辑我的帖子。当我使用普通RabbitTemplate而不是异步RabbitTemplate进行SendReceive时,我会得到一个错误:回复中没有相关头。将correlationData传递到消费者中的this.template.convertAndSend无法解决此问题。请参阅我答案的更新;我们可能应该将此功能构建到框架中。非常感谢您的帮助。这次成功了。我将在框架中添加一个特性请求。(我没有足够的声誉来支持你的回答,谢谢你);将其添加到框架中的工作已经在进行中。
@RabbitListener(queues = QueueConfig.QUEUENAME)
public TestResponse onReceive(TestEvent event) {
    Future<TestResponse> replyLater = proccessDataLater(event.getSomeData())
    return replyLater.get();
}
@RabbitListener(queues = QueueConfig.QUEUENAME)
public void onReceive(TestEvent event) {
   /* 
    * Some fictional RabbitMQ API call where i get a ReplyContainer which contains
    * the CorrelationID for the event. I can call replyContainer.reply(testResponse) later 
    * in the code without blocking the listener thread
    */        
    ReplyContainer replyContainer = AsyncRabbitTemplate.getReplyContainer()

    // ProcessDataLater calls reply on the container when done with its action
    proccessDataLater(event.getSomeData(), replyContainer);
}
@Configuration
@EnableRabbit
public class RabbitMQConfig implements RabbitListenerConfigurer {

    public static final String topicExchangeName = "exchange";

    @Bean
    TopicExchange exchange() {
        return new TopicExchange(topicExchangeName);
    }

    @Bean
    public ConnectionFactory rabbitConnectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
        connectionFactory.setHost("localhost");
        return connectionFactory;
    }

    @Bean
    public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
        return new MappingJackson2MessageConverter();
    }

    @Bean
    public RabbitTemplate rabbitTemplate() {
        final RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory());
        rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
        return rabbitTemplate;
    }

    @Bean
    public AsyncRabbitTemplate asyncRabbitTemplate() {
        return new AsyncRabbitTemplate(rabbitTemplate());
    }

    @Bean
    public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    @Bean
    Queue queue() {
        return new Queue("test", false);
    }

    @Bean
    Binding binding() {
        return BindingBuilder.bind(queue()).to(exchange()).with("foo.#");
    }

    @Bean
    public SimpleRabbitListenerContainerFactory myRabbitListenerContainerFactory() {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(rabbitConnectionFactory());
        factory.setMaxConcurrentConsumers(5);
        factory.setMessageConverter(producerJackson2MessageConverter());
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return factory;
    }

    @Override
    public void configureRabbitListeners(final RabbitListenerEndpointRegistrar registrar) {
        registrar.setContainerFactory(myRabbitListenerContainerFactory());
    }

}
@SpringBootApplication
public class So52173111Application {

    private final ExecutorService exec = Executors.newCachedThreadPool();

    @Autowired
    private RabbitTemplate template;

    @Bean
    public ApplicationRunner runner(AsyncRabbitTemplate asyncTemplate) {
        return args -> {
            RabbitConverterFuture<Object> future = asyncTemplate.convertSendAndReceive("foo", "test");
            future.addCallback(r -> {
                System.out.println("Reply: " + r);
            }, t -> {
                t.printStackTrace();
            });
        };
    }

    @Bean
    public AsyncRabbitTemplate asyncTemplate(RabbitTemplate template) {
        return new AsyncRabbitTemplate(template);
    }

    @RabbitListener(queues = "foo")
    public void listen(String in, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag,
            @Header(AmqpHeaders.CORRELATION_ID) String correlationId,
            @Header(AmqpHeaders.REPLY_TO) String replyTo) {

        ListenableFuture<String> future = handleInput(in);
        future.addCallback(result -> {
            Address address = new Address(replyTo);
            this.template.convertAndSend(address.getExchangeName(), address.getRoutingKey(), result, m -> {
                m.getMessageProperties().setCorrelationId(correlationId);
                return m;
            });
            try {
                channel.basicAck(tag, false);
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }, t -> {
            t.printStackTrace();
        });
    }

    private ListenableFuture<String> handleInput(String in) {
        SettableListenableFuture<String> future = new SettableListenableFuture<String>();
        exec.execute(() -> {
            try {
                Thread.sleep(2000);
            }
            catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            future.set(in.toUpperCase());
        });
        return future;
    }

    public static void main(String[] args) {
        SpringApplication.run(So52173111Application.class, args);
    }

}