Java Spring RabbitMQ-在具有@RabbitListener配置的服务上使用手动通道确认

Java Spring RabbitMQ-在具有@RabbitListener配置的服务上使用手动通道确认,java,rabbitmq,spring-amqp,spring-rabbit,Java,Rabbitmq,Spring Amqp,Spring Rabbit,如何在不使用自动确认的情况下手动确认消息。 有没有一种方法可以将其与@RabbitListener和@enablebrabbit配置风格一起使用。 大多数文档都告诉我们使用SimpleMessageListenerContainer以及ChannelAwareMessageListener。 然而,使用它,我们失去了注释所提供的灵活性。 我已将我的服务配置如下: @Service public class EventReceiver { @Autowired private MessageSe

如何在不使用自动确认的情况下手动确认消息。 有没有一种方法可以将其与
@RabbitListener
@enablebrabbit
配置风格一起使用。 大多数文档都告诉我们使用
SimpleMessageListenerContainer
以及
ChannelAwareMessageListener
。 然而,使用它,我们失去了注释所提供的灵活性。 我已将我的服务配置如下:

@Service
public class EventReceiver {

@Autowired
private MessageSender messageSender;

@RabbitListener(queues = "${eventqueue}")
public void receiveMessage(Order order) throws Exception {

  // code for processing order
}

我的兔子配置如下 任何关于如何适应手动通道确认以及上述配置风格的帮助都将不胜感激。 如果我们实现ChannelAwareMessageListener,那么onMessage签名将改变。
我们可以在服务上实现ChannelAwareMessageListener吗?

频道
添加到
@RabbitListener
方法中

@RabbitListener(queues = "${eventqueue}")
public void receiveMessage(Order order, Channel channel,
    @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {
    ...
}
并使用
basicAck
basicject
中的标记

编辑

@SpringBootApplication
@EnableRabbit
public class So38728668Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So38728668Application.class, args);
        context.getBean(RabbitTemplate.class).convertAndSend("", "so38728668", "foo");
        context.getBean(Listener.class).latch.await(60, TimeUnit.SECONDS);
        context.close();
    }

    @Bean
    public Queue so38728668() {
        return new Queue("so38728668");
    }

    @Bean
    public Listener listener() {
        return new Listener();
    }

    public static class Listener {

        private final CountDownLatch latch = new CountDownLatch(1);

        @RabbitListener(queues = "so38728668")
        public void receive(String payload, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag)
                throws IOException {
            System.out.println(payload);
            channel.basicAck(tag, false);
            latch.countDown();
        }

    }

}
application.properties:

spring.rabbitmq.listener.acknowledge-mode=manual

谢谢加里的帮助。我终于解决了这个问题。我记录这一点是为了其他人的利益。 这需要作为标准文档的一部分记录在Spring AMQP参考文档页面中。 服务等级如下

   @Service
    public class Consumer {

    @RabbitListener(queues = "${eventqueue}")
    public void receiveMessage(Order order, Channel channel) throws Exception {



 // the above methodname can be anything but should have channel as second signature

    channel.basicConsume(eventQueue, false, channel.getDefaultConsumer()); 
    // Get the delivery tag
    long deliveryTag = channel.basicGet(eventQueue, false).getEnvelope().getDeliveryTag();
    try {

      // code for processing order

    catch(Exception) {
     // handle exception
        channel.basicReject(deliveryTag, true);
    }
    // If all logic is successful 
    channel.basicAck(deliveryTag, false);
}

配置也已修改如下

public class RabbitApplication implements RabbitListenerConfigurer {

    private static final Logger log = LoggerFactory.getLogger(RabbitApplication .class);

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

    @Bean
    public MappingJackson2MessageConverter jackson2Converter() {
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        return converter;
    }

    @Bean
    public DefaultMessageHandlerMethodFactory myHandlerMethodFactory() {
        DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
        factory.setMessageConverter(jackson2Converter());
        return factory;
    }

    @Autowired
    private Consumer consumer;

    @Override
    public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
        registrar.setMessageHandlerMethodFactory(myHandlerMethodFactory());
    }

      ...
}

注意:无需配置Rabbitconnectionfactory或containerfactor等,因为注释隐式处理了所有这些问题。

以防需要使用ChannelAwareMessageListener类中的#onMessage()。那么你可以这样做

@Component
public class MyMessageListener implements ChannelAwareMessageListener {

@Override
public void onMessage(Message message, Channel channel) {
    log.info("Message received.");
    // do something with the message
    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}

对于兔子的配置

@Configuration
public class RabbitConfig {

public static final String topicExchangeName = "exchange1";

public static final String queueName = "queue1";

public static final String routingKey = "queue1.route.#";

@Bean
public ConnectionFactory connectionFactory() {
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
    connectionFactory.setUsername("xxxx");
    connectionFactory.setPassword("xxxxxxxxxx");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("vHost1");
    return connectionFactory;
}

@Bean
public RabbitTemplate rabbitTemplate() {
    return new RabbitTemplate(connectionFactory());
}

@Bean
Queue queue() {
    return new Queue(queueName, true);
}

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

@Bean
Binding binding(Queue queue, TopicExchange exchange) {
    return BindingBuilder.bind(queue).to(exchange).with(routingKey);
}


@Bean
public SimpleMessageListenerContainer listenerContainer(MyMessageListener myRabbitMessageListener) {
    SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
    listenerContainer.setConnectionFactory(connectionFactory());
    listenerContainer.setQueueNames(queueName);
    listenerContainer.setMessageListener(myRabbitMessageListener);
    listenerContainer.setAcknowledgeMode(AcknowledgeMode.MANUAL);
    listenerContainer.setConcurrency("4");
    listenerContainer.setPrefetchCount(20);
    return listenerContainer;
}

}

感谢您的建议,我们尝试了您的建议,并将其放入
频道。basicAck('100001',false)
。现在,不管我在上面的代码行中输入“true”还是“false”,侦听器和队列都会进入无限循环。你能帮我解决这个问题吗?我们终于解决了这个问题。我记录此内容是为了其他人的利益。我得到一个错误,即spring.rabbitmq.listener.acknowledge-mode是一个不推荐使用的属性。我最终在我的RabbitListenerContainerFactorybean上设置了这个属性,并以这种方式工作。在Spring Boot 2.0中,它可以是
Spring.rabbitmq.listener.simple.acknowledge模式
Spring.rabbitmq.listener.direct.acknowledge模式
,因为Spring AMQP现在支持两种容器类型。看,有人能给我解释一下为什么java程序员拒绝将导入放在他们的代码示例中吗?我觉得这样可以节省我几个小时。不-你不应该对频道发出
basicConsume
basicGet
basicGet
将获取另一条消息。侦听器容器已经在使用它,用于调用该方法的消息具有不同的传递标记。相反,使用
@Header(AmqpHeaders.DELIVERY_标记)长标记
。查看我的答案(编辑)。Gary删除了basicConsume和basicGet,并在basicAck/basicReject中使用了@Header(AmqpHeaders.DELIVERY_标记)长标记流停止工作。队列在无限循环中被一次又一次地获取,并且目标队列被填充。将代码还原回basicGet和basicConsume,它正在工作。但它不工作-您正在确认(并删除)下一条消息。因此,您的意思是我应该使用某种消费来避免它。现在还不清楚,因为你之前说过我们不应该使用basicConsume。我刚刚编写了一个快速Spring Boot应用程序,它的工作原理与我描述的一模一样——我用代码编辑了我的答案。完整的项目是和。如果在
basicAck
上设置断点,则可以在兔子管理UI中看到未确认的消息;一个问题是,为什么你需要这样做。如果您的代码与下面的答案类似,(失败时拒绝,否则确认),容器将使用自动确认模式自动为您执行此操作-如果侦听器抛出异常,消息将被拒绝;否则我会回答的。嗨,帕里,什么是PocrabitMessageListener?我不太明白你的问题,可以吗rephrase@java1977POCRabbitMessageListener是MyMessageListener。我的错是我没有注意到。谢谢。
@Configuration
public class RabbitConfig {

public static final String topicExchangeName = "exchange1";

public static final String queueName = "queue1";

public static final String routingKey = "queue1.route.#";

@Bean
public ConnectionFactory connectionFactory() {
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
    connectionFactory.setUsername("xxxx");
    connectionFactory.setPassword("xxxxxxxxxx");
    connectionFactory.setPort(5672);
    connectionFactory.setVirtualHost("vHost1");
    return connectionFactory;
}

@Bean
public RabbitTemplate rabbitTemplate() {
    return new RabbitTemplate(connectionFactory());
}

@Bean
Queue queue() {
    return new Queue(queueName, true);
}

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

@Bean
Binding binding(Queue queue, TopicExchange exchange) {
    return BindingBuilder.bind(queue).to(exchange).with(routingKey);
}


@Bean
public SimpleMessageListenerContainer listenerContainer(MyMessageListener myRabbitMessageListener) {
    SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
    listenerContainer.setConnectionFactory(connectionFactory());
    listenerContainer.setQueueNames(queueName);
    listenerContainer.setMessageListener(myRabbitMessageListener);
    listenerContainer.setAcknowledgeMode(AcknowledgeMode.MANUAL);
    listenerContainer.setConcurrency("4");
    listenerContainer.setPrefetchCount(20);
    return listenerContainer;
}