简体   繁体   English

通过一个 spring-rabbit 服务中的一个消费者同时处理多个 amqp 消息

[英]Handle multiple amqp messages concurrently through one consumer inside one spring-rabbit service

EDIT编辑

Just found out how to run multiple consumers inside one service:刚刚了解了如何在一项服务中运行多个消费者:

  @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");
  }

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?那么AMQP接收者的多个实例如何共享消费者总数呢? 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.所以我想将 concurrentConsumers 的全局数量设置为 10,运行 consumerSerivce 的 2 个实例,并让每个实例运行大约 5 个消费者。 Can this be managed by rabbitMq?这可以由 rabbitMq 管理吗?


I have a Spring service that consumes AMQP messages and calls a http resource for each message.我有一个 Spring 服务,它使用 AMQP 消息并为每条消息调用一个 http 资源。 After the http call completes another queue is called to either report error or done.在 http 调用完成后,调用另一个队列来报告错误或完成。 Only then will message handling complete and the next message be taken from the queue.只有这样,消息处理才会完成,下一条消息才会从队列中取出。

  // 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());
    }
  }

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.队列中有时有数百或数千条渲染消息,但 http 调用运行时间相当长,而且没有做太多事情。 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.这变得很明显,因为我可以通过运行多个服务实例来提高消息处理率,从而添加更多消费者并多次调用 http 端点。 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.然而,这会大大增加内存使用量(因为该服务使用 spring)来转发消息和处理结果。

So I thought, I'd do the http call asynchronously and return immediatly after accepting the message:所以我想,我会异步执行 http 调用并在接受消息后立即返回:

.httpCallRendererAsync(renderMessage)
    .subscribeOn(Schedulers.newThread())
    .subscribe(new Observer<String >() {
      public void onNext(String result) {
        messageProducer.sendDoneMessage(result);
      }
      public void onError(Throwable throwable) {
        messageProducer.sendErrorMessage(throwable.getMessage());
      }
    });

That however overloads the http endpoint which cannot deal with 1000 or more simultanous requests.然而,这会使无法处理 1000 个或更多同时请求的 http 端点过载。

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".我需要的是让我的 amqp 服务从队列中获取一定数量的消息,在单独的线程中处理它们,在每个线程中进行 http 调用并返回“已处理消息”。 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.然而,从队列中取出的消息量需要在该服务的多个实例之间共享,因此如果最大值为 10,则消息消耗是循环的,前 5 条奇数消息应由实例 1 处理,前 5 条偶数消息由实例 1 处理实例 2,一旦一个实例处理完消息,它就应该从队列中取出另一个实例。

What I found are things like prefetch with limts by consumer and by channel as described by rabbitmq .我发现的是像rabbitmq 所描述的那样按消费者和通道进行预取限制。 And the spring-rabbit implementation which uses prefetchCount and the transactionSize described here .以及使用 prefetchCount 和此处描述的 transactionSize 的 spring-rabbit 实现。 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".当然,它不会减少在我的异步场景中处理的消息数量,因为这些消息会立即被视为“已处理”。

  @Bean
  public RabbitListenerContainerFactory<SimpleMessageListenerContainer> 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")

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?可以使用 rabbitMq 和 spring 来完成吗? Or do I have to implemenent something in between.或者我必须在两者之间实施一些东西。

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.然后,我必须在扩展实例数量的同时使用环境变量手动配置限制。

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?那么AMQP接收者的多个实例如何共享消费者总数呢? 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.所以我想将 concurrentConsumers 的全局数量设置为 10,运行 consumerSerivce 的 2 个实例,并让每个实例运行大约 5 个消费者。 Can this be managed by rabbitMq?这可以由 rabbitMq 管理吗?

There is no mechanism in either RabbitMQ or Spring to support such a scenario automatically. RabbitMQ 或 Spring 中都没有自动支持这种情况的机制。 You can, however, change the concurrency at runtime ( setConcurrentConsumers() on the container) so you could use some external agent to manage the concurrency on each instance.但是,您可以在运行时更改并发性(容器上的setConcurrentConsumers() ),以便您可以使用一些外部代理来管理每个实例的并发性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM