无法使用MessageProducer.send()创建的JmsTemplate.receive()检索消息

时间:2016-01-26 00:05:27

标签: spring-jms

我正在使用Spring Boot启动嵌入式ActiveMQ代理,我正在使用spring-jms在运行时为主题动态注册JMS端点。我得到了那部分工作,当我创建一个JMS消息并将其发送到主题时,它最终被我的监听器消耗。到现在为止还挺好。在侦听器内部,如果在消息中设置了JMS头“JMSReplyTo”,我想向另一个主题发送一条简单的ACK消息。如果是,我创建一个MessageProducer,其目标设置为标题中设置的主题,然后发送ACK消息。代码似乎有效,或者至少我认为是因为我没有收到任何错误。但是,在我的测试用例中,在发送初始消息之后,我正在尝试读取另一个主题上的ACK,并且呼叫超时。我正在使用JmsTemplate发送原始消息,以及消耗ACK。我确定我错过了什么,但我不确定是什么。这是显示SessionAwareMessageListener的相关Spring Java配置部分:

@Bean(name = "sessionAwareMessageListener")
SessionAwareMessageListener<TextMessage> createSessionAwareMessageListener() {
  return new SessionAwareMessageListener<TextMessage>() {
    @Override
    public void onMessage(TextMessage message, Session session) throws JMSException {
      log.info(" Received: {} ", message.getText());

      // Prepare an ACK reply message
      final ActiveMQTextMessage textMessage = new ActiveMQTextMessage();
      textMessage.setText("ACK");

      // Send the ACK message back to the replyTo address of the incoming
      // message. 
      if (message.getJMSReplyTo() != null) {
        log.info("Sending ACK message to {}", message.getJMSReplyTo());
        // session.createTopic(message.getJMSReplyTo().toString());
        MessageProducer producer = session.createProducer(message.getJMSReplyTo());
        producer.send(textMessage);
      }
    }
  };
}

在我的Spock集成测试中,我正在创建一个MessageProducer并通过JmsTemplate实例发送消息:

...other stuff here...
MessageCreator messageCreator = new MessageCreator() {
  @Override
  public Message createMessage(Session session) throws JMSException {
    log.info("Sending text message")
    TextMessage message = session.createTextMessage("This is a test")
    message.setJMSReplyTo(new ActiveMQTopic("test"))
    return message
  }
}

... code that registers the JMS endpoint goes here...

JmsTemplate jmsTemplate = context.getBean(JmsTemplate)
assert jmsTemplate
jmsTemplate.send("bar", messageCreator)

发送消息成功,最终由前面显示的SessionAwareMessageListener处理。但是当我尝试使用JmsTemplate.receive()来检索ACK消息时,它会一直阻塞,直到调用超时,并返回null。在主题上没有为ACK消息注册特定的侦听器或动态创建的端点,因此我认为可以使用JmsTemplate.receive()来使用消息。显然我在这里做错了,但我无法弄清楚是什么。这是我正在尝试接收ACK的代码片段:

jmsTemplate.setReceiveTimeout(2000L)
Message received = jmsTemplate.receive("test")
assert received instanceof ActiveMQTextMessage
log.info("Received message: ${received}")
assert ((ActiveMQTextMessage) received).text == "ACK"

我很感激能够指出更好的方法。我真的只是在这一点上进行实验。我知道我需要动态注册端点,并可能将消息转发给其他主题。谢谢。

1 个答案:

答案 0 :(得分:1)

使用主题进行请求/回复消息传递通常不是一个好主意,尤其是在回复方面。

  1. 回复可能会在receive()订阅该主题之前到达;除非该订阅是持久的,否则代理将丢弃此类消息。
  2. 您通常只希望回复转发给发件人。
  3. 使用队列而不是主题,尤其是回复。

    考虑使用JmsTemplate进行回复,而不是创建自己的制作人;如果您正在使用DefaultMessageListenerContainer和会话事务处理(您应始终使用sessionTransacted与DMLC一起使用以避免丢失消息),模板将自动使用相同的会话。

    如果您希望对单个邮件做出多个响应,则可以在请求端使用主题,但是,除非订阅是持久的,否则回复的数量将是不确定的 - 您无法告诉生产者方如何许多消费者会收到它。