SpringBoot2使用RabbitMQ

RabbitMQ

基础知识

RabbitMQ 就是以 AMQP 协议实现的一种中间件产品,服务器端用 Erlang 语言编写

在这里插入图片描述

Queue

Queue(队列)是 RabbitMQ 的内部对象,用于存储消息,用下图表示。

在这里插入图片描述

RabbitMQ 中的消息都只能存储在 Queue 中,生产者(下图中的 P)生产消息并最终投递到 Queue 中,消费者(下图中的 C)可以从 Queue 中获取消息并消费。
在这里插入图片描述

多个消费者可以订阅同一个 Queue,这时 Queue 中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。

在这里插入图片描述

Exchange

之前我们看到生产者将消息投递到 Queue 中,实际上这在 RabbitMQ 中这种事情永远都不会发生。实际的情况是,生产者将消息发送到 Exchange(交换器,下图中的 X),由 Exchange 将消息路由到一个或多个 Queue 中(或者丢弃)。

在这里插入图片描述

Routing key

生产者在将消息发送给 Exchange 的时候,一般会指定一个 routing key,来指定这个消息的路由规则,而这个 routing key 需要与 Exchange Type 及 binding key 联合使用才能最终生效。

Binding

RabbitMQ 中通过 Binding 将 Exchange 与 Queue 关联起来,这样 RabbitMQ 就知道如何正确地将消息路由到指定的 Queue 了。

在这里插入图片描述

Binding key

在绑定(Binding)Exchange 与 Queue 的同时,一般会指定一个 binding key;
消费者将消息发送给 Exchange 时,一般会指定一个 routing key;当 binding key 与 routing key 相匹配时,消息将会被路由到对应的 Queue 中。

Exchange Types

  • fanout

转发消息到所有绑定队列

fanout 类型的 Exchange 路由规则非常简单,它会把所有发送到该 Exchange 的消息路由到所有与它绑定的 Queue 中。

在这里插入图片描述

  • direct

direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个 routing_key, 消息的routing_key 匹配时, 才会被交换器投送到绑定的队列中去.

在这里插入图片描述

  • topic

根据通配符匹配

它与 direct 类型的 Exchange 相似,也是将消息路由到 binding key 与 routing key 相匹配的 Queue 中,但这里的匹配规则有些不同,它约定:

  • routing key 为一个句点号. 分隔的字符串(我们将被句点号. 分隔开的每一段独立的字符串称为一个单词),如 “stock.usd.nyse”、”nyse.vmw”、”quick.orange.rabbit”

  • binding key 与 routing key 一样也是句点号. 分隔的字符串

  • binding key 中可以存在两种特殊字符 * 与#,用于做模糊匹配,

    • * 用于匹配一个单词,
    • #用于匹配多个单词(可以是零个)

在这里插入图片描述

以上图中的配置为例:

routingKey=”quick.orange.rabbit” 的消息会同时路由到 Q1 与 Q2
routingKey=”lazy.orange.fox” 的消息会路由到 Q1 与 Q2
routingKey=”lazy.brown.fox” 的消息会路由到 Q2
routingKey=”lazy.pink.rabbit” 的消息会路由到 Q2(只会投递给 Q2 一次,虽然这个 routingKey 与 Q2 的两个 bindingKey 都匹配)
routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit” 的消息将会被丢弃,因为它们没有匹配任何 bindingKey
  • Headers

设置 header attribute 参数类型的交换机

SpringBoot整合

目前使用的版本是 2.1.2.RELEASE

简单使用

1、配置 pom 包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2、配置文件

spring:
  rabbitmq:
    port: 5672
    host: 39.107.250.174
    username: admin #不修改的话默认的 guest
    password: admin  # guest
    virtual-host: my_vhost # /

3、队列配置

@Configuration
public class RabbitConfig {
    @Bean
    public Queue queue(){
        return new Queue("hello");
    }
}

4、发送者

Spring Boot 中会根据配置来注入其具体实现 (AmqpTemplate 的默认实现就是 RabbitTemplate)。

@Component
public class HelloSender {
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void send(){
        String context = "hello" + LocalDateTime.now();

        System.out.println("【sender】:" + context);

        amqpTemplate.convertAndSend("hello",context);// queue=hello
    }
}

5、接受者

@RabbitListener 配置在方法上,配置在类上可能会出错

@Component
public class HelloReceiver {
    @RabbitListener(queues = "hello")
    @RabbitHandler
    public void process(String hello){
        System.out.println("【receiver】*****:" + hello);
    }
}

6、测试

@SpringBootTest
@RunWith(SpringRunner.class)
public class RabbitMQHelloTest {

    @Autowired
    private HelloSender helloSender;


    @Test
    public void hello() {
        helloSender.send();
    }
}
  • @RabbitListener 可以标注在类上面,需配合 @RabbitHandler 注解一起使用

  • @RabbitListener 标注在类上面表示当有收到消息的时候,就交给 @RabbitHandler 的方法处理,具体使用哪个方法处理,根据 MessageConverter 转换后的参数类型

多对多使用

一对多发送

一个发送者,N个接受者,经过测试会均匀的将消息发送到N个接收者中

多对多发送

和一对多一样,接收端仍然会均匀接收到消息

高级使用

Spring Boot 以及完美的支持对象的发送和接收,不需要格外的配置。

一定要实现 Serializable 接口

//配置Queue
@Bean
public Queue objectQueue(){
    return new Queue("object");
}

···
//发送者
public void send(User user) {
	System.out.println("Sender object: " + user.toString());
	amqpTemplate.convertAndSend("object", user);
}

...

//接收者
@RabbitListener(queues ="object")
@RabbitHandler
public void process(User user) {
    System.out.println("Receiver object : " + user);
}

注意:

第一个参数表示交换机,第二个参数表示 routing key,第三个参数即消息。

amqpTemplate.convertAndSend("testTopicExchange","key1.a.c.key2", " this is  RabbitMQ!");

Topic Exchange

1、队列配置

  • 使用 queueMessages 同时匹配两个队列
  • queueMessage 只匹配 “topic.message” 队列
@Configuration
public class TopicRabbitConfig {
    final static String message = "topic.message";
    final static String messages = "topic.messages";

    @Bean
    public Queue queueMessage() {
        return new Queue(TopicRabbitConfig.message);
    }

    @Bean
    public Queue queueMessages() {
        return new Queue(TopicRabbitConfig.messages);
    }

    @Bean
    public TopicExchange exchange() {
        return new TopicExchange("exchange");
    }

    @Bean
    public Binding bindingExchangeMessage(Queue queueMessage,TopicExchange exchange){
        return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
    }

    @Bean
    public Binding bindingExchangeMessages(Queue queueMessages,TopicExchange exchange){
        return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");
    }
}

2、发送者

@Component
public class TopicSend {
    @Autowired
    private AmqpTemplate amqpTemplate;
    
    public void send1() {
        String context = "hi, i am message 1";
        System.out.println("Sender : " + context);
        amqpTemplate.convertAndSend("exchange", "topic.message", context);
    }

    public void send2() {
        String context = "hi, i am messages 2";
        System.out.println("Sender : " + context);
        amqpTemplate.convertAndSend("exchange", "topic.messages", context);
    }
}

3、接受者

@Component
public class TopicReceiver {

    @RabbitListener(queues="topic.message")
    @RabbitHandler
    public void receiverMessage(String msg){
        System.out.println("[receiverMessage ]:" + msg);
    }


    @RabbitListener(queues="topic.messages")
    @RabbitHandler
    public void receiverMessages(String msg){
        System.out.println("[receiverMessage ssssss ]*****:" + msg);
    }
}

结果:

  • 发送send1会匹配到topic.#topic.message 两个Receiver都可以收到消息,

  • 发送send2只有topic.#可以匹配所有只有Receiver2监听到消息

Fanout Exchange

Fanout 就是我们熟悉的广播模式或者订阅模式,给 Fanout 交换机发送消息,绑定了这个交换机的所有队列都收到这个消息。

1、Fanout 相关配置

@Configuration
public class FanoutConfig {
    @Autowired
    private AmqpTemplate amqpTemplate;

    @Bean
    public Queue aMessage() {
        return new Queue("fanout.a");
    }

    @Bean
    public Queue bMessage() {
        return new Queue("fanout.b");
    }

    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanoutExchange");
    }

    @Bean
    public Binding bindingExchangeA(Queue aMessage, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(aMessage).to(fanoutExchange);
    }

    @Bean
    public Binding bindingExchangeB(Queue bMessage, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(bMessage).to(fanoutExchange);
    }
}

这里使用了 A、B 两个队列绑定到 Fanout 交换机上面,发送端的 routing_key 写任何字符都会被忽略:

2、发送者

@Component
public class FanoutSend {
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendMessage(){
        String msg = "hello rabbit fanout";
        amqpTemplate.convertAndSend("fanoutExchange","",msg);
    }

}

3、接受者

@Component
public class FanoutReceiver {
    @RabbitListener(queues = "fanout.a")
    @RabbitHandler
    public void  receiverA(String msg){
        System.out.println("[receiverA]" + msg );
    }

    @RabbitListener(queues = "fanout.b")
    @RabbitHandler
    public void  receiverB(String msg){
        System.out.println("[receiverB]" + msg );
    }
}

结果:

  • receiverA,receiverB均能接受到消息
···
[receiverB]hello rabbit fanout
[receiverA]hello rabbit fanout

Direct Exchange

Topic几乎一致,随便看看就好

1、Direcrt 相关配置

定义了两个队列(direct.adirect.b),一个交换机(directExchange),

队列direct.adirectExchangeroutingA匹配

@Configuration
public class DirectConfig {
    @Bean
    public Queue directA(){
        return new Queue("direct.a");
    }

    @Bean
    public Queue directB(){
        return new Queue("direct.b");
    }

    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("directExchange");
    }

    @Bean
    public Binding bindingDirectA(Queue directA,DirectExchange directExchange){
        return BindingBuilder.bind(directA).to(directExchange).with("routingA");
    }

    @Bean
    public Binding bindingDirectB(Queue directB,DirectExchange directExchange){
        return BindingBuilder.bind(directB).to(directExchange).with("routingB");
    }
}

2、发送者

@Component
public class DirectReceiver {
    @RabbitListener(queues = "direct.a")
    @RabbitHandler
    public void receiverA(String msg){
        System.out.println(msg);
    }
    @RabbitListener(queues = "direct.b")
    @RabbitHandler
    public void receiverB(String msg){
        System.out.println(msg);
    }
}

3、接受者

@Component
public class DirectSend {
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendA() {
        String msg = "I am Direct [A]";
        amqpTemplate.convertAndSend("directExchange", "routingA", msg);
    }

    public void sendB() {
        String msg = "I am Direct [B]";
        amqpTemplate.convertAndSend("directExchange", "routingB", msg);
    }
}

结果:

  • receiverA只能接受到队列direct.a的消息
  • receiverB只能接受到队列direct.b的消息

@RabbitListener 注解声明 Binding

1、配置

@Component
public class QuickMQReceiver {
    //支持自动声明绑定,声明之后自动监听队列的队列,此时@RabbitListener注解的queue和bindings不能同时指定,否则报错
    //默认交换机是: direct
    @RabbitListener(bindings = @QueueBinding(value = @Queue("myQueue"), exchange = @Exchange(value = "myExchange"),key = "quick"))
    public void process(String msg){
        System.out.println("快速创建: " + msg);
    }
}

2、发送测试

@SpringBootTest
@RunWith(SpringRunner.class)
public class QuickMQTest {

    @Autowired
    private AmqpTemplate amqpTemplate;
    @Test
    public void send(){
        String msg = "哈哈哈,我是快速创建的";
        amqpTemplate.convertAndSend("myExchange","quick",msg);
    }
}

结果:

  • 收到消息
···
快速创建 哈哈哈,我是快速创建的

结尾

文章几乎来自于:

推荐:

硬核 文章

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页