【rabbitMQ】搭建MQ-开端

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

一、MQ的开端

MQ全称是【Message Queue】(消息队列)是一种应用程序对应用程序的消息通信,一端只管往队列不断发布信息,另一端只管往队列中读取消息,发布者不需要关心读取消息的谁,读取消息者不需要关心发布消息的是谁,各干各的互不干扰。

其主要作用包括

  • 应用解耦:一个业务需要多个模块共同实现,或一条消息有多个系统对应处理,只需要在主业务完成以后,发送一条MQ,其余模块消费MQ消息,即可实现业务,降低模块之间的耦合。
  • 异步提速:主业务执行结束后,从属业务通过MQ异步处理,减少业务的响应时间,提高用户体验。
  • 削峰填谷:高并发情况下,业务异步处理,提供高峰期业务处理能力,避免系统瘫痪。

话不多说,理论知识,百度一大堆,慢慢理解,直接开干 ~~

二、docker安装MQ

1、在线拉取镜像

docker pull rabbitmq:3-management

2、运行镜像

docker run \
 -e RABBITMQ_DEFAULT_USER=root \
 -e RABBITMQ_DEFAULT_PASS=root \
 --name mq \
 --hostname mq1 \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 rabbitmq:3-management
#指定mq面板的账号和密码,默认账号密码 guest
-e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=123456

3、访问页面

使用ip + 15672 运行MQ的浏览器页面

在这里插入图片描述
登陆进去页面
在这里插入图片描述

在开始代码之前,了解其中的一些名词

publisher:生产者(顾名思义:生产消息的商家)

consumer:消费者(顾名思义:消费消息的买家)

exchange:交换机,负责消息路由(可以比喻成 快递方)

queue:队列,存储消息(相当于 快递小哥)

在这里插入图片描述

三、SpringAMQP

直接干spring,原始工程过于繁杂,需要建立连接,指定主机名、端口号、vhost、用户名、密码;创建通道Channel;创建队列等操作

SpringAMQP是基于RabbitMQ封装的一套模板,并且还利用SpringBoot对其实现了自动装配,使用起来非常方便。

1、依赖、配置文件

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

**publisher(商家)**服务 和 **consumer(买家)**服务的application.yml中添加配置
注意的是,两个boot项目,区分开服务端口,以面冲突(service.port: XXX)

spring:
  rabbitmq:
    host: 192.168.200.250
    port: 5672
    username: guest
    password: guest

2、WorkQueue-工作队列

在这里插入图片描述

publisher(生产者)

新建配置文件,创建消息队列

@Configuration
public class RabbitmqConfig {
    /*
     * 单独创建队列 - 名字为:myQueue
     * */
    @Bean
    protected Queue queue() {
        Queue queue = new Queue("myQueue");
        return queue;
    }
}

发送消息的代码

@SpringBootTest
class DemoApplicationTests {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /*
    * 普通直接发送队列中
    * */
    @Test
    public void OneQueue(){
    	//参数一:需要指向发送的目标队列名称
    	//参数二:发送的内容
       rabbitTemplate.convertAndSend("myQueue","Hello World");
    }
}

consumer(消费者)

使用@RabbitListener注解进行监听队列是否有值,

@Component
public class DemeReceive {

    /*
    * 监听队列  -->> myQueue
    * */
    @RabbitListener(queues = "myQueue")
    public void demo(String msg) throws InterruptedException {
    	//参数msg: 就是消费队列消息的内容
        System.out.println("\033[32m demo获取到的消息 --->>> \" + " + msg + "\033[0m");
    }

2.1、WorkQueue-扩展

如果在生产方,生产大量的消息,而消费者绑定并且指定不同的睡眠时间来模拟日常代码执行速度

生产方:

    @Test
    public void OneQueue(){
		//循环发送10条数据,方面查看
        int index = 10;
        for (int i = 0; i < index; i++) {
            rabbitTemplate.convertAndSend("myQueue","这是我发的第" + i +"条消息");
        }
    }

消费方:

    /*
    * 监听者1号  --》》 myQueue
    * */
    @RabbitListener(queues = "myQueue")
    public void demo(String msg) throws InterruptedException {
        System.out.println("\033[32m demo获取到的消息 --->>> \" + " + msg + "\033[0m");
		//睡眠时间  20
        Thread.sleep(20);
    }

    /*
    * 监听者2号  --》》 myQueue
    *
    * */
    @RabbitListener(queues = "myQueue")
    public void demo2(String msg) throws InterruptedException {
        System.out.println("\033[31m demo2取到的消息 --->>> \" +" + msg + "\033[0m");
		//睡眠时间  100
        Thread.sleep(100);
    }

效果如下:
在这里插入图片描述
方便查看,颜色区分两个监听者----
可以看到监听者1很快完成了自己的5条消息。监听者2却在缓慢的处理自己的5条消息。
也就是说消息是平均分配给每个消费者,并没有考虑到消费者的处理能力。这样显然是有问题的。

如需要修改则需要变动配置文件,通过设置prefetch来控制消费者预取的消息数量,

spring:
  rabbitmq:
    host: 192.168.200.250
    username: guest
    password: guest
    listener:
      simple:
#      通过设置prefetch来控制消费者预取的消息数量
        prefetch: 1

每一个监听者,每次只处理一条数据,数据结束可以重新处理新的数据
效果如下:
在这里插入图片描述
能者多劳!!!

3、发布/订阅

在这里插入图片描述
可以看到,在订阅模型中,多了一个exchange角色,而且过程略有变化:

Publisher:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给exchange(交换机)

Exchange:交换机,图中的exchange。

  • 一方面,接收生产者发送的消息。
  • 另一方面,知道如何处理消息,
    Exchange有以下3种类型:
  • Fanout:广播,将消息交给所有绑定到交换机的队列。
  • Direct:定向,把消息交给符合指定routing key 的队列。
  • Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列)

3.1、Fanout

生产者

创建消息队列和交换机,之间进行绑定

@Configuration
public class RabbitmqConfig {

    //创建  消息队列1
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanoutExchange");
    }

    //创建  消息队列2
    @Bean
    public Queue fanoutQueue1() {
        return new Queue("fanoutQueue1");
    }

    //创建  fanout类型 交换机
    @Bean
    public Queue fanoutQueue2() {
        return new Queue("fanoutQueue2");
    }

    //通过 BindingBuilder 绑定消息队列1 到交换机上
    //参数一:需要绑定的消息队列
    //参数二:指定需绑定的交换机
    @Bean
    public Binding bindingFanoutQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }

    //通过 BindingBuilder 绑定队列2 到交换机上
    //参数一:需要绑定的消息队列
    //参数二:指定需绑定的交换机
    @Bean
    public Binding bingdingFanoutQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }

发送消息

 @Test
    public void fanoutQueue(){
    	//参数一:指定需要发送的交换机命长
    	//参数二:路由key,此类型交换机无需指定
    	//参数三:需要发送的内容
        rabbitTemplate.convertAndSend("fanoutExchange","","单独发送一条消息");
    }
消费者
    @RabbitListener(queues = "fanoutQueue1")
    public void demo3(String msg){
        System.out.println("\033[32m demo3获取到的消息 --->>> \" + " + msg + "\033[0m");
    }
    @RabbitListener(queues = "fanoutQueue2")
    public void demo4(String msg){

        System.out.println("\033[31m demo4取到的消息 --->>> \" +" + msg + "\033[0m");
    }

效果如下:
在这里插入图片描述

FanoutExchange 的会将消息路由到每个绑定的队列,每个队列绑定的消费者都可以或者到消息进行消费

3.2、Direct

在Fanout模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。
在这里插入图片描述

队列与交换机的绑定之间是需要指定一个RoutingKey(路由key),理解成一个暗号

消息的发送方在 向 Exchange发送消息时,也必须指定消息的 RoutingKey(暗号)。
Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routingkey与消息的 Routing key完全一致,才会接收到消息

生产者

创建消息队列和交换机,之间进行绑定

@Configuration
public class RabbitmqConfig {

    //创建  消息队列1
    @Bean
    public Queue directQueue1() {
        return new Queue("directQueue1");
    }

    //创建  消息队列2
    @Bean
    public Queue directQueue2() {
        return new Queue("directQueue2");
    }

    //创建  direct类型 交换机
    public DirectExchange directExchange() {
        return new DirectExchange("directExchange");
    }

    //通过 BindingBuilder 绑定消息队列1 到交换机上
    //参数一:需要绑定的消息队列
    //参数二:指定需绑定的交换机
    //并在绑定的同时,执行routingKey(暗号)
    @Bean
    public Binding bindingDirectQueue1(Queue directQueue1, DirectExchange directExchange) {
        return BindingBuilder.bind(directQueue1).to(directExchange).with("red");
    }

    //通过 BindingBuilder 绑定队列2 到交换机上
    //参数一:需要绑定的消息队列
    //参数二:指定需绑定的交换机
    //并在绑定的同时,执行routingKey(暗号)
    @Bean
    public Binding bindingDirectQueue2(Queue directQueue2, DirectExchange directExchange) {
        return BindingBuilder.bind(directQueue2).to(directExchange).with("black");
    }

发送消息

    @Test
    public void directQueue(){
        rabbitTemplate.convertAndSend("directExchange","red","发给红色方的消息");
    }
消费者
    @RabbitListener(queues = "directQueue1")
    public void demo5(String msg){
        System.out.println("\033[32m demo5获取到的消息 --->>> \" + " + msg + "\033[0m");
    }
    @RabbitListener(queues = "directQueue2")
    public void demo6(String msg){

        System.out.println("\033[31m demo6取到的消息 --->>> \" +" + msg + "\033[0m");
    }

效果如下:
在这里插入图片描述

此时 Direct交换机根据RoutingKey(暗号)判断路由给哪个队列,具体哪个消费者来进行消费

3.3、Topic

Topic类型的Exchange与Direct相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符

Routingkey 一般都是有一个或多个单词组成,多个单词之间以”.”分割。

#:匹配一个或多个词

*:匹配不多不少恰好1个词

其余内容同Direct交换机一样,在交换机和队列绑定时,做出微调,.with(XXXX)不再是具体的routingKey,而是由通配符组成的rontingKey


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值