Spring AMQP 随笔 7 Others

0. 天气掌握我的身体

最后的一篇了,各种高级的用法,有的我也妹听过

4.1.14. RabbitMQ REST API

When the management plugin is enabled, the RabbitMQ server exposes a REST API to monitor and configure the broker.
A Java Binding for the API is now provided. The com.rabbitmq.http.client.Client / com.rabbitmq.http.client.ReactorNettyClient is a standard, immediate, and, therefore, blocking API.

4.1.17. Message Listener Container Configuration

There are quite a few options for configuring a SimpleMessageListenerContainer (SMLC) and a DirectMessageListenerContainer (DMLC) related to transactions and quality(质量) of service, and some of them interact with each other.
Properties that apply to the SMLC, DMLC, or StreamListenerContainer (StLC) (see Using the RabbitMQ Stream Plugin are indicated by the check mark in the appropriate column.

The following table shows the container property names and their equivalent attribute names (in parentheses(括弧))) when using the namespace to configure a rabbit:listener-container/.
The type attribute on that element can be simple (default) or direct to specify an SMLC or DMLC respectively.
Some properties are not exposed by the namespace. These are indicated by N/A for the attribute.

see Table 3. Configuration options for a message listener container Property (Attribute) Description

4.1.18. Listener Concurrency

SimpleMessageListenerContainer

By default, the listener container starts a single consumer that receives messages from the queues.

You can see a number of properties and attributes that control concurrency - concurrentConsumers,
which creates that (fixed) number of consumers that concurrently process messages.
If it is changed while the container is running, consumers are added or removed as necessary to adjust to the new setting.

In addition, a new property called maxConcurrentConsumers has been added and the container dynamically adjusts the concurrency based on workload(工作量).
This works in conjunction(关联) with four additional properties: consecutive(连续的)ActiveTrigger, startConsumerMinInterval, consecutiveIdleTrigger, and stopConsumerMinInterval.

  • With the default settings, the algorithm to increase consumers works as follows:

If the maxConcurrentConsumers has not been reached
and an existing consumer is active for ten consecutive cycles
AND at least 10 seconds has elapsed since the last consumer was started,
a new consumer is started.
(A consumer is considered active if it received at least one message in batchSize * receiveTimeout milliseconds.)

  • With the default settings, the algorithm to decrease consumers works as follows:

If there are more than concurrentConsumers running
and a consumer detects ten consecutive timeouts (idle)
AND the last consumer was stopped at least 60 seconds ago,
a consumer is stopped.
(The timeout that A consumer is considered idle if it receives no messages in batchSize * receiveTimeout milliseconds.)

Practically, consumers can be stopped only if the whole container is idle for some time.
This is because the broker shares its work across all the active consumers.

Each consumer uses a single channel, regardless of the number of configured queues.

The concurrentConsumers and maxConcurrentConsumers properties can be set with the concurrency property

Using DirectMessageListenerContainer

With this container, concurrency is based on the configured queues and consumersPerQueue.
Each consumer for each queue uses a separate channel, and the concurrency is controlled by the rabbit client library.
By default, at the time of writing, it uses a pool of DEFAULT_NUM_THREADS = Runtime.getRuntime().availableProcessors() * 2 threads.

You can configure a taskExecutor to provide the required maximum concurrency.

4.1.19. Exclusive Consumer

You can configure the listener container with a single exclusive consumer.
This prevents other containers from consuming from the queues until the current consumer is cancelled. The concurrency of such a container must be 1.

When using exclusive consumers, other containers try to consume from the queues according to the recoveryInterval property and log a WARN message if the attempt fails.

4.1.20. Listener Container Queues

A number of improvements for handling multiple queues in a listener container.

Container can be initially configured to listen on zero queues. Queues can be added and removed at runtime.

  • The SimpleMessageListenerContainer recycles (cancels and re-creates) all consumers when any pre-fetched messages have been processed.
  • The DirectMessageListenerContainer creates/cancels individual consumer(s) for each queue without affecting consumers on other queues.

If not all queues are available, the container tries to passively(被动) declare (and consume from) the missing queues every 60 seconds.

Also, if a consumer receives a cancel from the broker (for example, if a queue is deleted) the consumer tries to recover, and the recovered consumer continues to process messages from any other configured queues.
Previously, a cancel on one queue cancelled the entire consumer and, eventually(最终), the container would stop due to the missing queue.

If you wish to permanently remove a queue, you should update the container before or after deleting to queue, to avoid future attempts trying to consume from it.

4.1.22. Multiple Broker (or Cluster) Support

@SpringBootApplication(exclude = RabbitAutoConfiguration.class)
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    CachingConnectionFactory cf1() {
        return new CachingConnectionFactory("localhost");
    }

    @Bean
    CachingConnectionFactory cf2() {
        return new CachingConnectionFactory("otherHost");
    }

    @Bean
    CachingConnectionFactory cf3() {
        return new CachingConnectionFactory("thirdHost");
    }

    @Bean
    SimpleRoutingConnectionFactory rcf(CachingConnectionFactory cf1,
            CachingConnectionFactory cf2, CachingConnectionFactory cf3) {

        SimpleRoutingConnectionFactory rcf = new SimpleRoutingConnectionFactory();
        rcf.setDefaultTargetConnectionFactory(cf1);
        rcf.setTargetConnectionFactories(Map.of("one", cf1, "two", cf2, "three", cf3));
        return rcf;
    }

    @Bean("factory1-admin")
    RabbitAdmin admin1(CachingConnectionFactory cf1) {
        return new RabbitAdmin(cf1);
    }

    @Bean("factory2-admin")
    RabbitAdmin admin2(CachingConnectionFactory cf2) {
        return new RabbitAdmin(cf2);
    }

    @Bean("factory3-admin")
    RabbitAdmin admin3(CachingConnectionFactory cf3) {
        return new RabbitAdmin(cf3);
    }

    @Bean
    public RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry() {
        return new RabbitListenerEndpointRegistry();
    }

    @Bean
    public RabbitListenerAnnotationBeanPostProcessor postProcessor(RabbitListenerEndpointRegistry registry) {
        MultiRabbitListenerAnnotationBeanPostProcessor postProcessor
                = new MultiRabbitListenerAnnotationBeanPostProcessor();
        postProcessor.setEndpointRegistry(registry);
        postProcessor.setContainerFactoryBeanName("defaultContainerFactory");
        return postProcessor;
    }

    @Bean
    public SimpleRabbitListenerContainerFactory factory1(CachingConnectionFactory cf1) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(cf1);
        return factory;
    }

    @Bean
    public SimpleRabbitListenerContainerFactory factory2(CachingConnectionFactory cf2) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(cf2);
        return factory;
    }

    @Bean
    public SimpleRabbitListenerContainerFactory factory3(CachingConnectionFactory cf3) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(cf3);
        return factory;
    }

    @Bean
    RabbitTemplate template(SimpleRoutingConnectionFactory rcf) {
        return new RabbitTemplate(rcf);
    }

    @Bean
    ConnectionFactoryContextWrapper wrapper(SimpleRoutingConnectionFactory rcf) {
        return new ConnectionFactoryContextWrapper(rcf);
    }

}

@Component
class Listeners {

    @RabbitListener(queuesToDeclare = @Queue("q1"), containerFactory = "factory1")
    public void listen1(String in) {

    }

    @RabbitListener(queuesToDeclare = @Queue("q2"), containerFactory = "factory2")
    public void listen2(String in) {

    }

    @RabbitListener(queuesToDeclare = @Queue("q3"), containerFactory = "factory3")
    public void listen3(String in) {

    }

}

We have declared 3 sets of infrastructure (connection factories, admins, container factories).
As discussed earlier, @RabbitListener can define which container factory to use; in this case, they also use queuesToDeclare which causes the queue(s) to be declared on the broker, if it doesn’t exist.
By naming the RabbitAdmin beans with the convention <container-factory-name>-admin, the infrastructure is able to determine which admin should declare the queue.
This will also work with bindings = @QueueBinding(…) whereby(据此) the exchange and binding will also be declared.
It will NOT work with queues, since that expects the queue(s) to already exist.


On the producer side, a convenient ConnectionFactoryContextWrapper class is provided,
to make using the RoutingConnectionFactory (see Routing Connection Factory) simpler.

As you can see above, a SimpleRoutingConnectionFactory bean has been added with routing keys one, two and three.
There is also a RabbitTemplate that uses that factory. Here is an example of using that template with the wrapper to route to one of the broker clusters.

@Bean
public ApplicationRunner runner(RabbitTemplate template, ConnectionFactoryContextWrapper wrapper) {
    return args -> {
        wrapper.run("one", () -> template.convertAndSend("q1", "toCluster1"));
        wrapper.run("two", () -> template.convertAndSend("q2", "toCluster2"));
        wrapper.run("three", () -> template.convertAndSend("q3", "toCluster3"));
    };
}

4.1.23. Debugging

Spring AMQP provides extensive logging, especially at the DEBUG level.

If you wish to monitor the AMQP protocol between the application and broker,
you can use a tool such as WireShark, which has a plugin to decode the protocol.
Alternatively, the RabbitMQ Java client comes with a very useful class called Tracer.
When run as a main, by default, it listens on port 5673 and connects to port 5672 on localhost.
You can run it and change your connection factory configuration to connect to port 5673 on localhost.
It displays the decoded protocol on the console. Refer to the Tracer Javadoc for more information.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值