SpringBoot2>04 - 集成RabbitMQ

消息中间件:

ActiveMQ,RabbitMQ,Kafka等都是常见的消息中间件,安全性从高到低,速度从低到高,Kafka在大数据中使用较多,RabbitMQ在银行系统中使用较多。主要应用在异步解耦、消息缓冲等场景中。

RabbitMQ简介:

由Erlang语言开发,实现AMQP(Advanced Message Queuing Protocol)的一种消息中间件。支持多种客户端,例如:Python、Ruby、.NET、Java、JMS、C、PHP等。

个人学习总结:
链接:【springboot、springcloud、docker 等,学习目录

结构:

来自百度百科:

提到消息中间件,我们常听到的是:消息生产者、消费者、队列。如图,RabbitMQ多了一个Exchange交换器,Exchange位于消息生产者和队列之间。

相关名词:

  • Producer: 消息生产者,图中最左边。将消息发送到Exchange中。

  • Consumer:消息消费者,图中最右边。订阅队列中的消息。

  • RabbitMQ Server:传输服务,维护着生产者到消费者之间的线路,保证消息按照指定的方式传输。

  • Exchange:交换器,将接收到的producer消息路由到指定队列(一或多个)。具有direct、fanout、topic、headers四种类型,每种类型对应不同的路由规则

  • Queue:队列,用于存放Exchange路由过来的消息,consumer从队列中订阅消息。

  • RoutingKey:路由规则,生产者发送消息到Exchange时指定,Exchange根据路由规则将消息发送到哪一个Queue中存储。

direct、fanout、topic 初步了解:

1、direct :直接模式,最简单的使用,一个消息只会有一个消费者。不需要 routingKey 规则匹配。

2、fanout:分列模式,将消息一次发给多个队列。不需要 routingKey 规则匹配。

3、topic:订阅模式,交换机中的消息根据匹配规则路由到对应的队列中。

集成RabbitMQ:

1、docker安装RabbitMQ:

# 1、获取镜像
docker pull rabbitmq:management
# 2、创建容器
docker run ‐di ‐‐name=tensquare_rabbitmq ‐p 5671:5617 ‐p 5672:5672 ‐p 4369:4369 ‐p 15671:15671 ‐p 15672:15672 ‐p 25672:25672 rabbitmq:management

监视画面:http://192.168.25.133:15672默认用户名、密码:guest

2、pom依赖:

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

3、application配置:

# 工程的端口
server:
  port: 8099

spring:
  rabbitmq:
    host: 192.168.25.133
    # mq 的端口
    port: 5672
    username: guest
    password: guest

直接模式(Direct):

1、队列配置:

@Configuration
public class DirectConfig {
    @Bean
    public Queue helloQueue(){
        return new Queue("hello");
    }
}

亦可在15672监视画面中配置队列。

2、Producer:

    @Autowired
    private RabbitTemplate rabbitTemplate;
    /**
     * 直接模式  最简单的使用
     * 一个消息只会有一个消费者,
     */
    @Test
    public void testSend(){
        // 多个消费者只有一个消费者接收到消息
        rabbitTemplate.convertAndSend("hello","直接模式测试");

        // 多个接收者  均匀的分布到消费者中
        /*for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend("hello","直接模式测试 :" + i);
        }*/
    }

3、consumer:

@Component
@RabbitListener(queues = "hello")
public class DirectCcustomer01 {
    @RabbitHandler
    public void showMessage(String message){
        System.out.println("DirectCcustomer01 >>>hello>>>接收到消息:"+message);
    }
}
@Component
@RabbitListener(queues = "hello")
public class DirectCcustomer02 {
    @RabbitHandler
    public void showMessage(String message){
        System.out.println("DirectCcustomer02 >>>hello>>>接收到消息:"+message);
    }
}

4、测试:

  • 发送一次:多个消费者只有一个消费者可以接收。

  • 发送多次:多个消费者均匀消费。

分列模式(Fanout):

1、队列配置:此处定义了三个队列测试,生产中根据实际需求定义。

@Configuration
public class FanoutConfig {

    /*********************** 定义队列 start ************************/
    @Bean
    public Queue fanoutA() {
        return new Queue("fanout.a");
    }

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

    @Bean
    public Queue fanoutC() {
        return new Queue("fanout.c");
    }
    /*********************** 定义队列 end ************************/

    /**
     * 分列式 交换器
     * 名称:fanoutExchange
     */
    @Bean
    FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanoutExchange");
    }

    /****************** 队列绑定交换机 start ********************/

    // fanoutA 与 fanoutExchange 绑定
    @Bean
    public Binding bindingExchangeWithA() {
        return BindingBuilder.bind(fanoutA()).to(fanoutExchange());
    }

    // fanoutB 与 fanoutExchange 绑定
    @Bean
    public Binding bindingExchangeWithB() {
        return BindingBuilder.bind(fanoutB()).to(fanoutExchange());
    }

    // fanoutC 与f anoutExchange 绑定
    @Bean
    public Binding bindingExchangeWithC() {
        return BindingBuilder.bind(fanoutC()).to(fanoutExchange());
    }

    /****************** 队列绑定交换机 end ********************/
}

2、Producer:

   /**
     * 分列模式 -- 将消息一次发给多个队列
     * 不需要 routingKey 规则匹配  直接模式的加强版
     */
    @Test
    public void testSendFanout(){
        rabbitTemplate.convertAndSend("fanoutExchange","", "分列模式发送的消息");
    }

3、consumer:对应三个消费者

​@Component
@RabbitListener(queues = "fanout.a")
public class FanoutConsumerA {
    @RabbitHandler
    public void showMessage(String message){
        System.out.println(">>>FanoutConsumerA >>>fanout.a>>>接收到消息:"+message);
    }
}
@Component
@RabbitListener(queues = "fanout.b")
public class FanoutConsumerB {
    @RabbitHandler
    public void showMessage(String message){
        System.out.println(">>>FanoutConsumerB >>>fanout.b>>>接收到消息:"+message);
    }
}
@Component
@RabbitListener(queues = "fanout.c")
public class FanoutConsumerC {
    @RabbitHandler
    public void showMessage(String message){
        System.out.println(">>>FanoutConsumerC >>>fanout.c>>>接收到消息:"+message);
    }
}

4、测试:

发送一次消息,FanoutConsumerA 、B、C都可以接收到消息。

订阅模式(Topic):

1、队列配置:

/**
 * @Auther: xf
 * @Date: 2019/1/3 18:46
 * @Description: 主题模式
 *
 * 任何发送到Topic Exchange的消息都会被转发到所有关心RouteKey中指定话题的Queue上
 * RouteKey:匹配规则,使每个消息队列只关心自己匹配的消息
 */
@Configuration
public class TopicConfig {

    /*********************** 定义队列 start ************************/
    @Bean
    public Queue topiocA() {
        return new Queue("topic.a");
    }

    @Bean
    public Queue topicB() {
        return new Queue("topic.b");
    }

    @Bean
    public Queue topicC() {
        return new Queue("topic.c");
    }
    /*********************** 定义队列 end ************************/


    /**
     * 主题模式 交换器
     * 名称:topicExchange
     */
    @Bean
    TopicExchange topicExchange() {
        return new TopicExchange("topicExchange");
    }

    /****************** 队列绑定交换机 start ********************/

    /**
     * topicA 与 topicExchange 绑定
     * routingKey : topic.coolron
     * 只会接收包含topic.msg的消息
     */
    @Bean
    public Binding bindingTopicExchangeWithA() {
        return BindingBuilder.bind(topiocA()).to(topicExchange()).with("topic.coolron");
    }

    /**
     * topicB 与 topicExchange 绑定
     * routingKey : topic.#
     * 只会接收 topic 开头的信息 topic、topic.AA、topic.AA.BB、topic.AA.BB.CC
     * # 匹配0个、一个或多个词
     */
    @Bean
    public Binding bindingTopicExchangeWithB() {
        return BindingBuilder.bind(topicB()).to(topicExchange()).with("topic.#");
    }

    /**
     * topicC 与 topicExchange 绑定
     * routingKey : topic.*
     * 只能接收类似 topic.AA、topic.BB、topic.CC 的消息
     * * 匹配一个词
     */
    @Bean
    public Binding bindingTopicExchangeWithC() {
        return BindingBuilder.bind(topicC()).to(topicExchange()).with("topic.*");
    }
    /****************** 队列绑定交换机 end ********************/
}

注意:"#"代表0个、一个或多个词、"*"代表一个词,"."分隔。

2、Producer:

    /**
     * 主题模式
     */
    @Test
    public void testSendTopic1(){
        // 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.a、b、c 都可以匹配到
        rabbitTemplate.convertAndSend("topicExchange","topic.coolron","主题模式");
    }

    @Test
    public void testSendTopic2(){
        // 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.b 可以匹配到 topic.#
        rabbitTemplate.convertAndSend("topicExchange","topic.cool.ron","主题模式");
    }

    @Test
    public void testSendTopic3(){
        // 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.b、c 可以匹配到 topic.# topic.*
        rabbitTemplate.convertAndSend("topicExchange","topic.ron","主题模式");
    }

    @Test
    public void testSendTopic4(){
        // 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.b、c 可以匹配到 topic.# topic.*,“” 代表一个词
        rabbitTemplate.convertAndSend("topicExchange","topic.","主题模式");
    }

    @Test
    public void testSendTopic5(){
        // 根据TopicConfig 中 RoutingKey 的配置 ,队列 topic.b 可以匹配到 topic.# 
        rabbitTemplate.convertAndSend("topicExchange","topic","主题模式");
    }

3、consumer:

/**
 * @Auther: xf
 * @Date: 2019/1/3 22:46
 * @Description: 主题模式 消费者 A
 */
​@Component
@RabbitListener(queues = "topic.a")
public class TopicCustomerA {
    @RabbitHandler
    public void showMessage(String message){
        System.out.println("TopicCustomerA>>>topic.a>>>接收到消息:"+message);
    }
}
@Component
@RabbitListener(queues = "topic.b")
public class TopicCustomerB {
    @RabbitHandler
    public void showMessage(String message){
        System.out.println("TopicCustomerB>>>topic.b>>>接收到消息:"+message);
    }
}
@Component
@RabbitListener(queues = "topic.c")
public class TopicCustomerC {
    @RabbitHandler
    public void showMessage(String message){
        System.out.println("TopicCustomerC>>>topic.c>>>接收到消息:" + message);
    }
}

4、测试结果:

  • testSendTopic1(topic.coolron):三个consumer都可以订阅到消息。规则:topic.coolron、topic.#、topic.*。

  • testSendTopic2(topic.cool.ron):只有TopicCustomerB订阅到消息。规则:topic.#。

  • testSendTopic3(topic.ron):TopicCustomerB、C订阅到消息。规则:topic.#、topic.*。

  • testSendTopic4(topic.):队列 B、C可以订阅到消息,“” 代表一个词 。规则topic.#  topic.*。

  • testSendTopic5(topic):队列 B 可以订阅到消息。规则:topic.#。

总结:

1、一条消息,直接模式只能一个消费者订阅、分列模式可多个消费者订阅、topic模式根据路由规则匹配订阅。
2、Exchange位于producer和queue之间。
3、Exchange根据匹配规则将消息路由到对应的Queue中存储。
4、routingKey,"#"代表0个、一个或多个、"*"代表一个词。上文中"topic."会匹配到B、C,因为“”代表一个词。“topic”只会匹配到B。

代码地址:

springboot-account2: 公众号 - Gitee.com


个人微信公众号,谢谢支持!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值