Home > Net >  Creating a seperate conatiner per queue
Creating a seperate conatiner per queue

Time:06-11

I have a use case where we have a strict need that all of the queues have a dedicated thread and consumer, which means each thread will only serve one queue.

I have read this accepted answer and the comment by Gary Russell. He mentioned creating a child application context for having all the features of @RabbitListener for achieving what I am trying. Still I didn't get how to do this when the queues are dynamically added during runtime.

If possible please point me to the relevant article for solving this in both the application context way(also how can I create the child contexts) and the MessageListenerAdapter way as advised.

CodePudding user response:

Here is some solution based on Spring Boot and child application context declaration.

  1. I have @RabbitListener in the component which will be declared in the child context:

     @EnableRabbit
     public class DynamicRabbitListener {
    
         @Bean
         public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
             return new RabbitAdmin(connectionFactory);
         }
    
         @RabbitListener(queuesToDeclare = @Queue(name = "${dynamic.queue}", autoDelete = "true"))
         public void listen(String payload, @Header(AmqpHeaders.CONSUMER_QUEUE) String queueName) {
             System.out.printf("Received %s from queue %s%n", payload, queueName);
         }
    
     }
    
  2. We need @EnableRabbit to register respective annotation processor to the child application context and be able to trigger the proper lifecycle.

  3. W need AmqpAdmin in this context to be able to declare dynamic queues.

  4. Both of those aspects give us some isolation in the processing and logic.

  5. Now this is how I declare those contexts:

     @Bean
     ApplicationRunner applicationRunner(ConfigurableApplicationContext parent, RabbitTemplate rabbitTemplate) {
         return args -> {
             for (int i = 0; i < 10; i  ) {
                 String dynamicQueue = "dynamicQueue#"   i;
                 AnnotationConfigApplicationContext childApplicationContext =
                         new AnnotationConfigApplicationContext();
                 childApplicationContext.setParent(parent);
                 childApplicationContext.register(DynamicRabbitListener.class);
    
                 ConfigurableEnvironment environment = parent.getEnvironment();
                 MapPropertySource propertySource =
                         new MapPropertySource("dynamic.queues", Map.of("dynamic.queue", dynamicQueue));
                 environment.getPropertySources().addLast(propertySource);
                 childApplicationContext.setEnvironment(environment);
    
                 childApplicationContext.refresh();
    
                 rabbitTemplate.convertAndSend(dynamicQueue, "test data #"   i);
             }
         };
     }
    

Pay attention to the propertySource to achieve a dynamic requirements for every child application context with its specific @RabbitListener.

The output of my program is like this:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.0)

Received test data #0 from queue dynamicQueue#0
Received test data #1 from queue dynamicQueue#1
Received test data #2 from queue dynamicQueue#2
Received test data #3 from queue dynamicQueue#3
Received test data #4 from queue dynamicQueue#4
Received test data #5 from queue dynamicQueue#5
Received test data #6 from queue dynamicQueue#6
Received test data #7 from queue dynamicQueue#7
Received test data #8 from queue dynamicQueue#8
Received test data #9 from queue dynamicQueue#9
  • Related