保持spring上下文有效,直到消耗JMS消息

时间:2018-03-14 16:40:17

标签: java spring-boot jms activemq

我有一个与height - JMSSpring Boot相关的标准设置。它工作正常,直到我尝试进行简单的集成测试。经过一些调查后,我发现在第一个JMS消息被消耗后,Spring上下文和嵌入式代理都被关闭,无论消费过程中是什么,都会触发另一个事件。我可以通过在测试设置中添加ActiveMQ连接选项来解决代理问题,即

useShutdownHook=false

我正在寻找的东西基本上是一种迫使测试“保持活力”的方法。直到消耗掉所有JMS消息(在这种情况下它们只是两个)。我理解整个设置的异步性质,但是在测试期间,生成和使用这些消息的所有结果会很有帮助。

以下是我的设置,虽然它相当简单。

spring.activemq.broker-url = vm://broker?async=false&broker.persistent=false&broker.useShutdownHook=false

然后我有一个消息驱动的POJO来监听给定的事件:

@EnableJms
public class ActiveMqConfig {

    @Bean
    public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
        JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
        jmsTemplate.setMessageConverter(messageConverter);
        return jmsTemplate;
    }

    @Bean
    public MessageConverter messageConverter() {
        MappingJackson2MessageConverter messageConverter = new MappingJackson2MessageConverter();
        messageConverter.setTargetType(MessageType.TEXT);
        messageConverter.setTypeIdPropertyName("_type");
        return messageConverter;
    }
}

还有一个:

@JmsListener(destination = "events")
public void applicationSubmitted(MyType event) {
    // do some work with the event here

    jmsTemplate.convertAndSend("commands", mymessage);
}

我试过的一件事就是在邮件发送后添加延迟,即@JmsListener(destination = "commands") public void onCommand(TextMessage textMessage) { } 。然而,这非常不可靠并且还会减慢测试速度,因为执行可能需要不到50毫秒。以下是测试本身。除非等待取消注释,否则我永远不会进入第二个事件监听器,因为应用程序上下文关闭,测试结束,消息被“忘记”#34;。

sleep(200)

2 个答案:

答案 0 :(得分:3)

嗯,这是基于异步消息交换测试系统时的标准问题。通常,它会在您跳过的测试部分中解决 - then部分。

问题在于,在您的测试中,您通常希望系统做一些有用的事情,例如:在DB中进行更改,向另一个系统发送一个休息调用,在另一个队列中发送消息等。我们可以等待一段时间直到它发生,不断检查结果 - 如果结果是在我们的时间窗口内实现的已经设定 - 然后我们可以假设测试已经过去了。

这种方法的伪代码如下:

for (i to MAX_RETRIES; i++) {
   checkThatTheChangesInDBHasBeenMade();
   checkThatTheRestCallHasBeenMade();
   checkThatTheMessageIsPostedInAnotherQueue();

   Thread.sleep(50ms);
}

这种方式在最佳情况下,您的测试将在50ms内完成。在更糟糕的情况下,它将失败,这将花费MAX_RETRIES * 50ms的时间来执行测试。

另外,我应该提一下,有一个很好的工具叫awaitility提供了很好的API(顺便说一句,它支持groovy DSL)来处理异步世界中的这类问题:

await().atMost(5, SECONDS).until(customerStatusIsUpdated());

答案 1 :(得分:1)

我认为问题的根源是异步事件处理。发送活动后,您的测试即将结束。这当然会导致Spring上下文和代理关闭。 JMS侦听器正在另一个线程中运行。你必须找到一种等待它们的方法。否则,您的线程(这是您的测试用例)就完成了。

我们在上一个项目中遇到了类似的问题并编写了一个小实用程序,它帮助了我们很多。 JMS提供了“浏览”队列并查看它是否为空的能力:

public final class JmsUtil {

    private static final int MAX_TRIES = 5000;
    private final JmsTemplate jmsTemplate;

    public JmsUtil(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }

    private int getMessageCount(String queueName) {
        return jmsTemplate.browseSelected(queueName, "true = true", (s, qb) -> Collections.list(qb.getEnumeration()).size());
    }

    public void waitForAll(String queueName) {
        int i = 0;
        while (i <= MAX_TRIES) {
            if (getMessageCount(queueName) == 0) {
                return;
            }
            i++;
        }
}

使用此实用程序,您可以执行以下操作:

def "My event is successfully handled"() {

        given:
        def event = new MyEvent()

        when:
        jmsTemplate.convertAndSend("events", event)
        jmsUtility.waitForAll("events"); // wait until the event has been consumed
        jmsUtility.waitForAll("commands"); // wait until the command has been consumed

        then:
        1 == 1
    }

注意:此实用程序假定您将JMS消息发送到队列。通过浏览队列,我们​​可以检查它是否为空。如果是某个主题,您可能需要进行另一项检查。所以要注意这一点!