title: Handle multiple amqp messages concurrently through one consumer inside one spring-rabbit service
body: <h2>EDIT</h2>
  
  <p>Just found out how to run multiple consumers inside one service:</p>
  
  <pre><code>  @Bean
    SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
      SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
      container.setConnectionFactory(connectionFactory);
      container.setQueueNames(RENDER_QUEUE);
      container.setConcurrentConsumers(concurrentConsumers); // setting this in env
      container.setMessageListener(listenerAdapter);
      return container;
    }
  
    @Bean
    MessageListenerAdapter listenerAdapter(RenderMessageConsumer receiver) {
      return new MessageListenerAdapter(receiver, "reciveMessageFromRenderQueue");
    }
  </code></pre>
  
  <p>Now the only question that remains is: how can I have a global limit? So how do multiple instances of the AMQP receiver share the total number of consumers? So I want to set a global number of concurrentConsumers to 10, run 2 instances of the consumerSerivce and have each instance run around 5 consumers. Can this be managed by rabbitMq?</p>
  
  <hr>
  
  <p>I have a Spring service that consumes AMQP messages and calls a http resource for each message.
  After the http call completes another queue is called to either report error or done. Only then will message handling complete and the next message be taken from the queue. </p>
  
  <pre><code>  // simplified
    @RabbitListener(queues = RENDER_QUEUE)
    public void reciveMessageFromRenderQueue(String message) {
      try {
        RenderMessage renderMessage = JsonUtils.stringToObject(message, RenderMessage.class);
        String result = renderService.httpCallRenderer(renderMessage);
        messageProducer.sendDoneMessage(result);
      } catch (Exception e) {
        logError(type, e);
        messageProducer.sendErrorMessage(e.getMessage());
      }
    }
  </code></pre>
  
  <p>There are at times hundreds or thousands of render messages in the queue but the http call is rather long running and not doing much. This becomes obvious as I can improve the message handling rate by running multiple instances of the service thus adding more consumers and calling the http endpoint multiple times. One instance has exactly one consumer for the channel so the number of instances is equal to the number of consumers. However that heavily increases memory usage (since the service uses spring) for just forwarding a message and handling the result.</p>
  
  <p>So I thought, I'd do the http call  asynchronously and return immediatly after accepting the message:</p>
  
  <pre><code>.httpCallRendererAsync(renderMessage)
      .subscribeOn(Schedulers.newThread())
      .subscribe(new Observer&lt;String &gt;() {
        public void onNext(String result) {
          messageProducer.sendDoneMessage(result);
        }
        public void onError(Throwable throwable) {
          messageProducer.sendErrorMessage(throwable.getMessage());
        }
      });
  </code></pre>
  
  <p>That however overloads the http endpoint which cannot deal with 1000 or more simultanous requests.</p>
  
  <p>What I need is for my amqp service to take a certain amount of messages from the queue, handle them in separate threads, make the http call in each of them and return with "message handled". The amount of messages taken from the queue however needs to be shared between multiple instances of that service, so if maximum is 10, message consumption is round robin, the first 5 odd messages should be handled by instance one and the first 5 even messages by instance 2 and as soon as one instance finishes handling the message it should take another one from the queue.</p>
  
  <p>What I found are things like prefetch with limts by consumer and by channel <a href="https://www.rabbitmq.com/consumer-prefetch.html" rel="nofollow noreferrer">as described by rabbitmq</a>. And the spring-rabbit implementation which uses prefetchCount and the transactionSize <a href="https://docs.spring.io/spring-amqp/docs/1.4.5.RELEASE/reference/html/amqp.html" rel="nofollow noreferrer">described here</a>. That however does not seem to do anything for a single running instance. It will not spawn additional threads to handle more messages concurrently. And of course it will not reduce the number of messages handled in my async scenario since those messages are immediatly considered "handled".</p>
  
  <pre><code>  @Bean
    public RabbitListenerContainerFactory&lt;SimpleMessageListenerContainer&gt; prefetchContainerFactory(ConnectionFactory rabbitConnectionFactory) {
      SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
      factory.setConnectionFactory(rabbitConnectionFactory);
      factory.setPrefetchCount(5);
      factory.setTxSize(5);
      return factory;
    }
  
    // and then using
    @RabbitListener(queues = RENDER_QUEUE, containerFactory = "prefetchContainerFactory")
  </code></pre>
  
  <p>The most important requirement for me seems to be that multiple messages should be handled in one instance while the maximum of concurrently handled messages should be shared between instances.
  Can that be done using rabbitMq and spring? Or do I have to implemenent something in between.</p>
  
  <p>In an early stage it might be acceptable to just have concurrent message handling in one instance and not share that limit. Then I'll have to configure the limit manually using environment variables while scaling the number of instances. </p>