Announcement Announcement Module
Collapse
No announcement yet.
RabbitAdmin autoStartup = false and calling init method Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • RabbitAdmin autoStartup = false and calling init method

    I have not been able to find any guidance online or the forum on the following problem.

    I have some common code in a Jar file where I declare the following:

    Code:
    @Configuration
    public class CommonConfig
    {
    
        @Bean
        public ConnectionFactory connectionFactory()
        {
            final CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");
            return connectionFactory;
        }
    
       @Bean
        public AmqpAdmin amqpAdmin()
        {
            final RabbitAdmin admin = new RabbitAdmin(rabbitConnectionFactory());
            return admin;
        }
    
       public SimpleMessageListenerContainer configureContainer(Object messageHandler, String... queuesNames){
           SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
           container.setConnectionFactory(connectionFactory());
           container.setQueueNames(queueNames)
           container.setMessageListener(new MessageListenerAdapter(messageHandler));
           return container;
       }
    
    }
    I then have a message producer in WebappA that declares the following:

    Code:
    @Configuration
    public class ServerConfig extends CommonConfig
    {
    
       @Bean
       public Exchange myExchange(){
            return new TopicExchange("foobarExchange");
       }
    }
    Then there is a message consumer in WebappB with the following:


    Code:
    @Configuration
    public class ClientConfig extends CommonConfig
    {
    
       @Bean
       public MyMessageHandler myMessageHandler(){
          return new MyMessageHandler();
       }
    
       @Bean 
       public SimpleMessageListenerContainer listenerContainer() {
          return configureContainer(myMessageHandler(), myQueue().getName())
       }
    
      @Bean
      public Queue myQueue(){
          return new Queue("myQueue");
      }
    
      @Bean
      public Binding myBinding(){
           return new Binding(myQueue().getName(), DestinationType.QUEUE, "foobarExchange", "abc.def", Collections.<String, Object> emptyMap());
      }
    }
    My problem here is that both the server and client webapps are started at the same time. Since the exchange is declared in the ServerConfig file, the exchange won't exist until the message producer produces its first message since the AmqpAdmin bean creates the exchange lazily.

    So then the binding of the Queue to the exchange in the client will fail.

    This issue will probably repeat itself in other places as I have many webapps that declare Exchanges and other webapps that consume from them. I rather have the owning webapps of an Exchange declare that exchange instead of the consumer.

    My reaction was to change the AmqpAdmin Bean in the CommonConfig to the following:

    Code:
        @Bean(initMethod = "initialize")
        public AmqpAdmin amqpAdmin()
        {
            final RabbitAdmin admin = new RabbitAdmin(rabbitConnectionFactory());
            admin.setAutoStartup(false);
            return admin;
        }
    I am wondering if I will encounter any issues with this approach. So far my preliminary tests tell me no.

    The other thing I thought about doing was declaring the exchange as follows in the ServerConfig:

    Code:
    @Configuration
    public class ServerConfig extends CommonConfig
    {
    
       @Bean
       public Exchange myExchange(){
            Exchange ex =  new TopicExchange("foobarExchange");
            amqpAdmin().declareExchange(ex);
            return ex;
       }
    }
    What about this approach? Which would be best? My doubt in all this is in calling the broker during the initialization of the application context. I don't want to run into any issues.

    My last resort option is to declare the exchange in the constructor or init method of the message producer:

    Code:
    @Component
    public class MyMessageProducerImpl implements MyMessageProducer, InitializingBean  {
    
       @Resource(name="amqpAdmin")
       private AmqpAdmin admin;
    
       @Resource(name="myExchange")
       private Exchange myExchange;
    
       @Override
       public void afterPropertiesSet() {
             admin.declareExchange(myExchange);
       }
    }
    The reason I rather not do this is because in the common config (which is part of even more common code), I rather not expose the internal of communicating with the broker. WebappA and WebappB and WebappX should just need to deal with creating exchanges, queues, and bindings. All webapps are following a similar approach to message consumption so even the configuration of the SimpleMessageListenerContainer is standardized so I am providing a convenience function to create one in the common config.

    Any thoughts on all of this? It is possible that I will distribute the common code to other apps where another instance of the RabbitMQ broker exists and so I don't want to include the bean definitions of every Exchange in the common code as other RabbitMQ instances don't need to declare those exchanges.

  • #2
    Given that you are using durable, non-auto-delete exchanges; this should only happen the first time; can you not simply pre-declare your exchanges to avoid it?

    Given that the client Binding already needs the exchange name, what is the downside of adding the declaration to the client as well? (Of course, the client would then know the type of exchange, not just its name).

    Rather that calling initialize() as an init method, you could call getConnection() on the connection factory which would call initialize() internally in the onCreate() callback while creating the cached connection.

    Comment

    Working...
    X