Spring Boot 整合 RabbitMQ

761 阅读4分钟

基本概念

Publisher(发布者)

发布者 (或称为生产者) 负责生产消息并将其投递到指定的交换器上。

Consumer(消费者)

消费者, 消费生产者产生的消息。

Exchange(交换器)

接收Producer发来的消息, 并转发到对应的 Queue, RabbitMQ 有四种类型的 Exchange , 不同类型的 Exchange 对消息的转发方式不同。

  • Direct Exchange: Direct Exchange 会对消息的 routing key 和 Queue 绑定到 Exchange 的 binding key 比对, 将消息转发给完全匹配(等值)的 Queue 。即 Queue 的 binding key = routing key 。如下图,当消息的 RountingKey 为 orange 时,消息会被路由到 Q1 队列;当消息的 RountingKey 为 black 或 green 时,消息会被路由到 Q2 队列。

一个交换器绑定多个队列时,它们的 BindingKey 是可以相同的,如下图。此时当消息的 RountingKey 为 black 时,消息会同时被路由到 Q1 和 Q2 队列。

  • Topic Exchange: Topic Exchange 运行将 routing key 和 binding key 进行通配符匹配。

    routing key 和 binding key 由多个单词使用 . 进行连接

    • BindingKey 支持两个特殊符号:#* 。其中 * 用于匹配一个单词, # 用于匹配零个或者多个单词。

    以下是官方文档中的示例,交换器与队列的绑定情况如图所示,此时的路由情况如下:

    • 路由键为 lazy.orange.elephant 的消息会发送给所有队列;

    • 路由键为 quick.orange.fox 的消息只会发送给 Q1 队列;

    • 路由键为 lazy.brown.fox 的消息只会发送给 Q2 队列;

    • 路由键为 lazy.pink.rabbit 的消息只会发送给 Q2 队列;

    • 路由键为 quick.brown.fox 的消息与任何绑定都不匹配;

    • 路由键为 orangequick.orange.male.rabbit 的消息也与任何绑定都不匹配。

  • Fanout Exchange: Fanout Exchange 是消息广播的模式, 不会去匹配路由键,直接把消息投递到所有绑定到 fanout 交换器中的队列</上,它就像一个广播站一样,它会向所有收听广播的用户发送消息。

  • Headers Exchange(极少使用)

###Queue(消息队列)

存储消息的队列。其通过 binding key 与 Exchange 绑定。

Routing Key(路由键)

消息的一个属性,可以看作是消息的类型(根据业务自定义),比如将程序不同级别的日志作为消息发送时,error级别的消息就可以使用 log.error 作为 routing key。

Binding Key(绑定键)

Queue 与 Exchang 绑定时的一个属性,可以看作 Queue 对哪种类型的业务消息感兴趣, Exchange会根据消息的 routing key 和 binding key 决定是否将该消息转发给一个 Queue。

Virtual Host(虚拟主机)

RabbitMQ 通过虚拟主机来实现逻辑分组和资源隔离, 可以认为每个虚拟主机是一个独立的命名空间, 拥有独立的队列、交换器和绑定关系。用户可以按照不同业务场景建立不同的虚拟主机,虚拟主机之间是完全独立的,你无法将 vhost1 上的交换器与 vhost2 上的队列进行绑定,这可以极大的保证业务之间的隔离性和数据安全。默认的虚拟主机名为 /

Spring Boot 整合 RabbitMQ

1、引入依赖

Spring Boot 集成 RabbitMQ 非常简单,如果只是简单的使用配置非常少,Spring Boot 提供了spring-boot-starter-amqp 项目对消息各种支持。

<!--rabbbitMQ相关依赖-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2、配置 RabbitMQ 服务器连接地址、端口号、账号、密码

# rabbitmq服务器连接端口 (默认为5672)
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

注: guest为rabbitmq的默认用户名和密码, 只能登陆本机的rabbitmq服务器, 若mq非本机部署的情况下, 需要添加用户并设置用户权限。

3、添加 RabbitMQ 配置类, 配置Queue、Exchange、Binding

@Configuration
public class RabbitMQConfig {
    /**
     * 用于接收warn级别的日志消息的队列
     * @return
     */
    @Bean
    public Queue warnQueue() {
        // 参数为队列的name, 其等价于 new Queue(name, true, false, false)
        // 即:durable = true, exclusive = false, autoDelete = false
        return new Queue("warn.queue");
    }

    /**
     * 用于接收error级别的日志消息的队列
     * @return
     */
    @Bean
    public Queue errorQueue() {
        return new Queue("error.queue");
    }

    /**
     * 用于接收所有级别的日志消息的队列
     * @return
     */
    @Bean
    public Queue allQueue() {
        return new Queue("all.queue");
    }

    /**
     * 创建一个交换机
     * @return
     */
    @Bean
    public TopicExchange topicExchange(){
        // 参数为exchange的名称, 其等价于 new TopicExchange(name, true, false)
        // 即:durable = true, autoDelete = false
        return new TopicExchange("log.topic.exchange");
    }

    @Bean
    public Binding binding(){
        // 将队列于交换机绑定, 绑定三要素: Queue, Exchange, Binding Key
        return BindingBuilder.bind(warnQueue()).to(topicExchange()).with("log.warn");
    }

    @Bean
    public Binding binding1(){
        return BindingBuilder.bind(errorQueue()).to(topicExchange()).with("log.error");
    }
    
	/**
     * 使用log.*来匹配log.warn和log.error的日志消息
     * @return
     */
    @Bean
    public Binding binding2(){
        return BindingBuilder.bind(allQueue()).to(topicExchange()).with("log.*");
    }
}

注: 发送消息时会在rabbitmq服务器中创建这里配置的Queue、 Exchange、Binding。

4、发送消息

发送消息使用 AmqpTemplate ,直接注入即可。

@RunWith(SpringRunner.class)
@SpringBootTest
public class SenderTest {
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Test
    public void sendWarnLog(){
        // 参数依次为: 目标exchange, 消息的routingKey, 消息体
        amqpTemplate.convertAndSend("log.topic.exchange", "log.warn", "this is a warn log");
    }
}

运行测试方法成功发送消息, 我们可以通过 rabbitmq 的控制台来查看:

图中可以看到我们配置的三个队列以及交换机已经创建成功, 并且我们发送的警告级别的日志消息已经被转发到了 all.queue 和 warn.queue 中。

5、消费消息

@Service
@Slf4j
public class Consumer {
    /**
     * 指定消费的队列
     */
    @RabbitListener(queues = "warn.queue")
    public void consume(String message){
        log.info("消费消息: {}", message);
    }
}

注: 示例中发送的消息为字符串类型, 若要发送对象, 对象需要实现Serializable接口。