Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent @RabbitListener in spring-rabbit from trying to connect to server during integration test

I want to run some acceptance tests for my services that are using rabbitMq but I want to ignore all that require inter-service communication (amqp).

The problem however is that Spring tries to connect to the (non-exisiting) rabbit host on startup so it can register its consumers. It does that for each method that is annotated with @RabbitListener which can get quite annoying with the long timeout this has if I have more than one listener in my service.

How can I reduce this timeout or even prevent @RabbitListener connection all together?

Our (simplified) Rabbit Config:

@Configuration
@EnableRabbit
public class RabbitMqConfig {

    public RabbitMqConfig(
            @Value("${rabbitmq.host}") String rabbitHost,
            @Value("${rabbitmq.port}") int rabbitPort,
            @Value("${exchange.name}") String exchange) {
        this.rabbitHost = rabbitHost;
        this.rabbitPort = rabbitPort;
        this.exchange= exchange;
    }

  @Bean
  DirectExchange directExchangeBean() {
    return new DirectExchange(this.exchange, true, false);
  }

  @Bean
  public ConnectionFactory connectionFactory() {
    CachingConnectionFactory connectionFactory = new CachingConnectionFactory(rabbitHost);
    connectionFactory.setPort(rabbitPort);
    return connectionFactory;
  }

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


  @Bean
  public Queue itemDoneQueue() {
    return new Queue(ITEM_DONE_QUEUENAME, true);
  }

  @Bean
  Binding itemDoneBinding() {
    return BindingBuilder.bind(itemDoneQueue()).to(directExchangeBean()).with(ITEM_DONE_KEY);
  }

}

Properties

rabbitmq.host=192.168.42.100
rabbitmq.port=5672
exchange.name=myExchange

The Listener:

  @RabbitListener(queues = ITEM_DONE_QUEUENAME)
  public void receiveMessageFromItemDoneQueue(String message) {
    // do the work
  }

The Test:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class}) 
public abstract class RabbitTest {

Really nothing special here. Obviously during testing the rabbit host is unavailable. That is fine. I want to ignore the fact. And quickly.

I've tried

spring.rabbitmq.connection-timeout=1

But that didn't change anything.

Using

spring.rabbitmq.listener.simple.auto-startup=false

neither does anything.

Using

spring.autoconfigure.exclude:org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration

just kills my application context loading with spring complaining about a NoSuchBeanDefinitionException: No bean named 'rabbitListenerContainerFactory' available

Any ideas? Thanks!

like image 761
Pete Avatar asked Jul 05 '17 13:07

Pete


5 Answers

I would expect the spring.rabbitmq.listener.simple.auto-startup=false to work - are you sure you're not trying to connect to Rabbit with some other code? Can you provide a DEBUG log to show the problem when that is set to false?

You can use the JUnit BrokerRunning @Rule to skip any tests that need a real RabbitMQ broker.

like image 154
Gary Russell Avatar answered Oct 26 '22 11:10

Gary Russell


I've had a similar problem, but solved it with

spring.rabbitmq.listener.direct.auto-startup=false

SpringBoot version 2.2.4.RELEASE

Spring framework version 5.2.3.RELEASE

like image 41
Dejan Radovanović Avatar answered Oct 26 '22 09:10

Dejan Radovanović


Instead of

spring.rabbitmq.listener.simple.auto-startup=false

I have used the below (with out the word 'simple' in the prop name) and it did work for me

spring.rabbitmq.listener.auto-startup=false

like image 20
MaheshGudivada Avatar answered Oct 26 '22 09:10

MaheshGudivada


Thanks for your input Gary. I already found a solution that works for me. For some reason the connection timeout for the Consumer initialization is not exposed in the CachingConnectionFactory I used nor is the property used that I tried (spring.rabbitmq.connection-timeout).

What I do now is initialize the base ConnectionFactory and pass it to the CachingConnectionFactory (Having of course no clue where the difference is but that is the case most of the time with spring):

  @Bean
  public ConnectionFactory connectionFactory() {
    com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();
    connectionFactory.setConnectionTimeout(this.connectionTimeout);
    connectionFactory.setHost(this.rabbitHost);
    connectionFactory.setPort(this.rabbitPort);
    CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(
        connectionFactory);
    return cachingConnectionFactory;
  }

Now I can set the timeout to 1 during my acceptence tests causing the consumer initialization to fail quickly and to a higher value for deployment.

like image 22
Pete Avatar answered Oct 26 '22 11:10

Pete


If the property spring.rabbitmq.listener.simple.auto-startup=false does not have effect, you might be defining your own SimpleRabbitListenerContainerFactory bean

Check how this bean is defined in the RabbitAnnotationDrivenConfiguration.rabbitListenerContainerFactory()

The SimpleRabbitListenerContainerFactoryConfigurer binds together theSimpleRabbitListenerContainerFactory and properties defined in your application.properties (among other things)

If you use your own definition, then be sure to use something along the lines of

@Bean
SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
    SimpleRabbitListenerContainerFactoryConfigurer containerFactoryConfigurer, 
    ConnectionFactory connectionFactory) {

    SimpleRabbitListenerContainerFactory listenerContainerFactory =
            new SimpleRabbitListenerContainerFactory();
    containerFactoryConfigurer.configure(listenerContainerFactory, connectionFactory);

    return listenerContainerFactory;
}
like image 22
Volodymyr Belozorov Avatar answered Oct 26 '22 10:10

Volodymyr Belozorov