Spring rabbitlistner使用注释语法停止侦听队列

时间:2015-12-17 10:37:46

标签: spring rabbitmq amqp spring-amqp spring-rabbit

一位同事和我正在使用Spring开发一个需要从RabbitMQ队列中获取消息的应用程序。我们的想法是使用(通常很好的)弹簧注释系统来使代码易于理解。我们让系统使用@RabbitListner注释工作,但我们想要按需获取消息。 @RabbitListner注释不会这样做,只是在消息可用时才会收到消息。需求取决于"准备情况"客户应该"得到"来自队列的消息停止列出并处理该消息。然后确定它是否准备好接收新的并重新连接到队列。

我们一直在尝试使用spring-amqp / spring-rabbit模块手动执行此操作,虽然这可能是我们真的希望使用spring执行此操作。经过几个小时的搜索和浏览文档后,我们无法找到答案。

以下是我们目前收到的代码:

@RabbitListener(queues = "jobRequests")
public class Receiver {

@Autowired
private JobProcessor jobProcessor;

@RabbitHandler
public void receive(Job job) throws InterruptedException, IOException {
    System.out.println(" [x] Received '" + job + "'");
    jobProcessor.processJob(job);
}

}

工作处理器:

@Service
public class JobProcessor {

@Autowired
private RabbitTemplate rabbitTemplate;

public boolean processJob(Job job) throws InterruptedException, IOException {
    rabbitTemplate.convertAndSend("jobResponses", job);

    System.out.println(" [x] Processing job: " + job);

    rabbitTemplate.convertAndSend("processedJobs", job);

    return true;
}

}

换句话说,当Receiver收到作业时,它应该停止侦听新作业并等待作业处理器完成,然后开始列出新消息。

我们重新创建了空指针异常,这是我们用来从服务器端发送的代码。

@Controller
public class MainController {

@Autowired
RabbitTemplate rabbitTemplate;

@Autowired
private Queue jobRequests;

@RequestMapping("/do-job")
public String doJob() {

    Job job = new Job(new Application(), "henk", 42);

    System.out.println(" [X] Job sent: " + job);

    rabbitTemplate.convertAndSend(jobRequests.getName(), job);

    return "index";
    }
}

然后是客户端的接收代码

@Component
public class Receiver {

@Autowired
private JobProcessor jobProcessor;

@Autowired
private RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry;

@RabbitListener(queues = "jobRequests")
public void receive(Job job) throws InterruptedException, IOException, TimeoutException {

    Collection<MessageListenerContainer> messageListenerContainers = rabbitListenerEndpointRegistry.getListenerContainers();

    for (MessageListenerContainer listenerContainer :messageListenerContainers) {
        System.out.println(listenerContainer);
        listenerContainer.stop();
    }

    System.out.println(" [x] Received '" + job + "'");
    jobProcessor.processJob(job);

    for (MessageListenerContainer listenerContainer :messageListenerContainers) {
        listenerContainer.start();
    }
   }
}

更新的作业处理器

@Service
public class JobProcessor {

public boolean processJob(Job job) throws InterruptedException, IOException {

    System.out.println(" [x] Processing job: " + job);

    return true;
}

}

和堆栈跟踪

[x] Received 'Job{application=com.olifarm.application.Application@aaa517, name='henk', id=42}'
[x] Processing job: Job{application=com.olifarm.application.Application@aaa517, name='henk', id=42}
Exception in thread "SimpleAsyncTaskExecutor-1" java.lang.NullPointerException
2015-12-18 11:17:44.494 at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.isActive(SimpleMessageListenerContainer.java:838)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:93)
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1301)
    at java.lang.Thread.run(Thread.java:745)
 WARN 325899 --- [cTaskExecutor-1] o.s.a.r.l.SimpleMessageListenerContainer : Consumer raised exception, processing can restart if the connection factory supports it

java.lang.NullPointerException: null
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.isActive(SimpleMessageListenerContainer.java:838) ~[spring-rabbit-1.5.2.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$700(SimpleMessageListenerContainer.java:93) ~[spring-rabbit-1.5.2.RELEASE.jar:na]
    at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1195) ~[spring-rabbit-1.5.2.RELEASE.jar:na]
    at java.lang.Thread.run(Thread.java:745) [na:1.7.0_91]

听众停止工作,我们确实收到了一份新工作但是当它试图重新启动时,NPE就会被抛出。我们检查了rabbitMQ日志,发现连接关闭了大约2秒,然后自动重新打开,即使我们将线程置于作业处理器的睡眠状态。这可能是问题的根源?然而,错误并没有破坏程序,并且在抛出之后接收器仍然能够接收新的作业。我们在这里滥用这个机制还是这个有效的代码?

1 个答案:

答案 0 :(得分:3)

要按需获取消息,通常最好使用rabbitTemplate.receiveAndConvert()而不是监听器;这样你就可以在收到消息时完全控制。

version 1.5开始,您可以将模板配置为阻止一段时间(或直到消息到达)。否则,如果没有消息,它会立即返回null

侦听器实际上是为消息驱动的应用程序设计的。

如果您可以阻止侦听器中的线程直到作业完成,则不会再传递任何消息 - 默认情况下,容器只有一个线程。

如果在作业完成之前无法阻止线程,出于某种原因,您可以通过Endpoint Registry获取对它的引用来stop()/start()侦听器容器。

通常最好在单独的线程上停止容器。