在Spring Boot中配置多个消息侦听器容器

时间:2018-01-12 18:07:28

标签: spring-boot rabbitmq spring-amqp spring-rabbit

不确定我是否只是密集,但我正在尝试配置spring(没有XML配置)来连接RabbitMQ。我可以通过创建一个SmartMessageListenerContainer来使用来自我的队列的消息来使其正常工作,但我磕磕绊绊的是如何创建一组容器来侦听同一队列。我的理解是你得到一个容器,每个队列有一个消息监听器,但你可以配置多个容器来监听同一个队列。我的目标是实现RabbitMQ docs概述的工作队列策略(一个队列,多个工作者)。

我试图实现的是每个消息传递(prefetch = 1)的一个消息监听器/工作器以及绑定到单个队列的消息容器池,以有效地创建我的工作池。最终,这一切都需要是动态的,因为我的服务的每个实例都需要适当调整到预期的工作量。有些情况可能只需要几个工人,而其他人可能需要很多工人。这就是为什么为每个容器声明一个bean在我的情况下并不真正起作用,因为我不知道我需要多少钱。另外,做一些类似的东西似乎很时髦(这就是我见过的大多数例子):

@Bean
SimpleMessageListenerContainer container1() {...}

@Bean
SimpleMessageListenerContainer container2() {...}

@Bean
SimpleMessageListenerContainer container3() {...}

下面是我当前的代码,我注册了一个返回容器集合的bean。这个方法自动被调用并按预期创建所有容器,但容器似乎永远不会自动启动(我很清楚我明白为什么因为我手动创建容器)。我试图在创建后手动调用每个容器上的start()方法,但是当达到这一小段代码时,每个容器有60秒的延迟:

AsyncMessageProcessingConsumer
    private FatalListenerStartupException getStartupException() throws TimeoutException, InterruptedException {
            this.start.await(60000L, TimeUnit.MILLISECONDS);
        return this.startupException;
    }

但我确实看到一篇提到需要实现SmartLifeCycle接口的帖子。我当然可以在下面的配置类中执行此操作,然后在调用start()方法时调用我创建的每个容器上的start()方法。这是我唯一的选择,还是我在这里完全遗漏了什么?

我的兔子配置类:

@Configuration
public class RabbitConfiguration {
  private final List<SimpleMessageListenerContainer> listenerContainers;
  private final boolean useDurableQueues;
  private final String exchangeName;
  private final String inboundQueueName;
  private final String outboundQueueName;
  private final String host;
  private final String virtualHost;
  private final String userName;
  private final String password;
  private final int maxWorkers;

  public RabbitConfiguration(
      @Value( "${com.unwiredrev.remotelink.rabbitmq.exchangeName}" )
      final String exchangeName,
      @Value( "${com.unwiredrev.remotelink.rabbitmq.inboundQueueName}" )
      final String inboundQueueName,
      @Value( "${com.unwiredrev.remotelink.rabbitmq.outboundQueueName}" )
      final String outboundQueueName,
      @Value( "${com.unwiredrev.remotelink.rabbitmq.priorityQueueName}" )
      final String priorityQueueName,
      @Value( "${com.unwiredrev.remotelink.rabbitmq.username}" )
      final String userName,
      @Value( "${com.unwiredrev.remotelink.rabbitmq.password}" )
      final String password,
      @Value( "${com.unwiredrev.remotelink.rabbitmq.host}" )
      final String host,
      @Value( "${com.unwiredrev.remotelink.rabbitmq.virtualHost}" )
      final String virtualHost,
      @Value( "${com.unwiredrev.remotelink.rabbitmq.useDurableQueues:true}" )
      final boolean useDurableQueues,
      @Value( "${com.unwiredrev.remotelink.rabbitmq.maxWorkers:1}")
      final int maxWorkers ) {
    this.exchangeName = exchangeName;
    this.inboundQueueName = inboundQueueName;
    this.outboundQueueName = outboundQueueName;
    this.useDurableQueues = useDurableQueues;
    this.host = host;
    this.virtualHost = virtualHost;
    this.userName = userName;
    this.password = password;
    this.maxWorkers = maxWorkers;

    this.listenerContainers = new ArrayList<>( maxWorkers );
  }

  public String getExchangeName() {
    return exchangeName;
  }

  public String getInboundQueueName() {
    return inboundQueueName;
  }

  public String getOutboundQueueName() {
    return outboundQueueName;
  }

  public boolean useDurableQueues() {
    return useDurableQueues;
  }

  public String getHost() {
    return host;
  }

  public Optional<String> getVirtualHost() {
    return Optional.ofNullable( StringUtils.trimToNull( this.virtualHost ) );
  }

  public String getUserName() {
    return userName;
  }

  public String getPassword() {
    return password;
  }

  public int getMaxWorkers() {
    return maxWorkers;
  }

  @Bean
  Collection<SimpleMessageListenerContainer> messageListenerContainers( @NotNull ConnectionFactory connectionFactory, @NotNull RabbitAdmin rabbitAdmin ) {
    IntStream.range( 0, getMaxWorkers() ).forEach( idx -> {
      final SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
      container.setQueueNames( inboundQueueName );
      container.setConnectionFactory( connectionFactory );
      container.setMessageListener( new MessageReceiver() );
      container.setAcknowledgeMode( AcknowledgeMode.MANUAL );
      container.setRabbitAdmin( rabbitAdmin );

      listenerContainers.add( container );
    } );

    return listenerContainers;
  }

  @Bean
  RabbitAdmin rabbitAdmin( @NotNull ConnectionFactory connectionFactory ) {
    return new RabbitAdmin( connectionFactory );
  }

  @Bean
  ConnectionFactory connectionFactory() {
    final CachingConnectionFactory connectionFactory = new     CachingConnectionFactory( getHost() );
    connectionFactory.setUsername( getUserName() );
    connectionFactory.setPassword( getPassword() );
    getVirtualHost().ifPresent( connectionFactory::setVirtualHost );

    return connectionFactory;
  }

  @Bean( name = "rlClientExchange")
  TopicExchange getExchange() {
    return new TopicExchange( getExchangeName(), true, false );
  }

  @Bean( name = "rlClientInboundQueue" )
  Queue inboundQueue() {
    return new Queue( getInboundQueueName(), useDurableQueues() );
  }

  @Bean
  Queue outboundQueue() {
    return new Queue( getOutboundQueueName(), useDurableQueues() );
  }

  @Bean
  Binding inboundQueueBinding( @NotNull final TopicExchange exchange ) {
    if( exchange == null ) {
      throw new IllegalArgumentException( "Rabbit topic exchange cannot be null." );
    }

    return BindingBuilder.bind( inboundQueue() )
        .to( exchange )
        .with( getInboundQueueName() );
  }

  @Bean
  Binding outboundQueueBinding( @NotNull final TopicExchange exchange ) {
    if( exchange == null ) {
      throw new IllegalArgumentException( "Rabbit topic exchange cannot be null." );
    }

    return BindingBuilder.bind( outboundQueue() )
        .to( exchange )
        .with( getOutboundQueueName() );
  }
}

0 个答案:

没有答案