【Spring Boot】MQ的常见用法&概念 🔥

715 阅读4分钟
  • Exchange: 指定消息按什么规则,路由到哪个队列
  • Binding: 把 Exchange 和 Queue 按照路由规则绑定起来
  • Queue: 消息队列载体,每个消息都会被投入到一个或多个队列

借用的图

PS: 图是借用的

Exchange

AMQP 协议中的核心思想就是生产者和消费者的解耦,生产者从不直接将消息发送给队列。生产者通常不知道是否一个消息会被发送到队列中,只是将消息发送到一个交换机。先由 Exchange 来接收,然后 Exchange 按照特定的策略转发到 Queue 进行存储。Exchange 就类似于一个交换机,将各个消息分发到相应的队列中。

Exchange 就是根据这个 RoutingKey 和当前 Exchange 所有绑定的 Binding 做匹配,如果满足匹配,就往 Exchange 所绑定的 Queue 发送消息,这样就解决了我们向 RabbitMQ 发送一次消息,可以分发到不同的 Queue。

Fanout Exchange

Fanout Exchange 会忽略 RoutingKey 的设置,直接将 Message 广播到所有绑定的 Queue 中。

Direct Exchange

Direct Exchange 是 RabbitMQ 默认的 Exchange,完全根据 RoutingKey 来路由消息。设置 Exchange 和 Queue 的 Binding 时需指定 RoutingKey(一般为 Queue Name),发消息时也指定一样的 RoutingKey,消息就会被路由到对应的Queue。

Topic Exchange

Topic Exchange 和 Direct Exchange 类似,也需要通过 RoutingKey 来路由消息,区别在于Direct Exchange 对 RoutingKey 是精确匹配,而 Topic Exchange 支持模糊匹配。分别支持*和#通配符,*表示匹配一个单词,#则表示匹配没有或者多个单词。

Headers Exchange

Headers Exchange 会忽略 RoutingKey 而根据消息中的 Headers 和创建绑定关系时指定的 Arguments 来匹配决定路由到哪些 Queue。

Headers Exchange 的性能比较差,而且 Direct Exchange 完全可以代替它,所以不建议使用。

Default Exchange

Default Exchange 是一种特殊的 Direct Exchange。当你手动创建一个队列时,后台会自动将这个队列绑定到一个名称为空的 Direct Exchange 上,绑定 RoutingKey 与队列名称相同。有了这个默认的交换机和绑定,使我们只关心队列这一层即可,这个比较适合做一些简单的应用。

举例

package com.meatball.receiver;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Description:  .<br>
 * <p>Created Time: 2020/10/27 15:15 </p>
 * Exchange: 指定消息按什么规则,路由到哪个队列
 * Binding: 把 Exchange 和 Queue 按照路由规则绑定起来
 * Queue: 消息队列载体,每个消息都会被投入到一个或多个队列
 *
 * @author <a href="mail to: mengxiangyuancc@gmail.com" rel="nofollow">孟祥元</a>
 */
@Configuration
public class Declare {

    /**
     * 用户队列
     *
     * @param userQueueName 用户队列名
     * @return
     */
    @Bean
    public Queue userQueue(
        @Value("${app.rabbitmq.queue.user}") String userQueueName,
        @Value("${app.rabbitmq.exchange.common-dead-letter}") String commonDeadLetterExchangeName) {
        return QueueBuilder
            .durable(userQueueName)
            //声明该队列的死信消息发送到的 交换机 (队列添加了这个参数之后会自动与该交换机绑定,并设置路由键,不需要开发者手动设置)
            .withArgument("x-dead-letter-exchange", commonDeadLetterExchangeName)
            //声明该队列死信消息在交换机的 路由键
            .withArgument("x-dead-letter-routing-key", "user-dead-letter-routing-key")
            .build();
    }

    /**
     * 用户队列对应的死信队列
     * 用户队列产生死信 --> 死信交换机`common-dead-letter-exchange` --> 该用户死信队列
     * 用这个队列来接收user-queue的死信消息
     *
     * @return
     */
    @Bean
    public Queue userDeadLetterQueue(@Value("${app.rabbitmq.queue.user-dead-letter}") String userDeadLetterQueueName) {
        return QueueBuilder
            .durable(userDeadLetterQueueName)
            .build();
    }

    /**
     * 通用死信交换机
     *
     * @param commonDeadLetterExchangeName 通用死信交换机名
     * @return
     */
    @Bean
    public Exchange commonDeadLetterExchange(
        @Value("${app.rabbitmq.exchange.common-dead-letter}") String commonDeadLetterExchangeName) {
        return ExchangeBuilder
            .topicExchange(commonDeadLetterExchangeName)
            .durable(true)
            .build();
    }

    /**
     * 用户交换机
     *
     * @param userExchangeName 用户交换机名
     * @return
     */
    @Bean
    public Exchange userExchange(@Value("${app.rabbitmq.exchange.user}") String userExchangeName) {
        return ExchangeBuilder
            .topicExchange(userExchangeName)
            .durable(true)
            .build();
    }

    /**
     * 用户队列与交换机绑定
     * 用户交换机 --> 符合routingKey的消息分配给用户队列
     * 如:rabbitTemplate.convertAndSend(userExchange, "user.abc", user);
     *
     * @param userQueue    用户队列名
     * @param userExchange 用户交换机名
     * @return
     */
    @Bean
    public Binding userBinding(Queue userQueue, Exchange userExchange) {
        return BindingBuilder
            .bind(userQueue)
            .to(userExchange)
            .with("user.*")
            .noargs();
    }


    /**
     * 死信队列绑定死信交换机
     * 通用死信交换机 --> 符合routingKey的消息分配给用户死信队列
     *
     * @param userDeadLetterQueue      user-queue对应的死信队列
     * @param commonDeadLetterExchange 通用死信交换机
     * @return
     */
    @Bean
    public Binding userDeadLetterBinding(Queue userDeadLetterQueue, Exchange commonDeadLetterExchange) {
        return BindingBuilder
            .bind(userDeadLetterQueue)
            .to(commonDeadLetterExchange)
            .with("user-dead-letter-routing-key")
            .noargs();
    }
}
  • Queue是创建的队列。
  • Exchange是交换机。
  • MQ提供者将消息发送到Exchange
  • MQ消费者订阅Queue,消费其中的消息。
  • BindingExchangeQueue绑定在一起,将MQ提供者发送到Exchange的消息按照规则分配到Queue