spring boot整合rabbitmq以及使用场景和集群搭建

本文介绍了如何在SpringBoot项目中整合RabbitMQ,详细阐述了RabbitMQ的五种工作模型,并给出了具体实现代码。同时,讨论了RabbitMQ在异步处理、应用解耦和流量削峰等场景下的应用,以及RabbitMQ集群的搭建,包括普通集群和镜像集群的架构及优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

个人印象笔记地址:https://app.yinxiang.com/fx/c19556bc-e80f-4f4c-bf1f-04595af67180

项目github地址:https://github.com/ztb0312/southwind.git

SpringBoot整合RabbitMQ

搭建初始环境

1. 引入依赖

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

2.设置yml文件

spring:
  application:
    name: rabbitmq-springboot
  rabbitmq:
    host: 192.168.64.130
    port: 5672
    username: ems
    password: 123456
    virtual-host: /ems
 
注意:

消息模板-RabbitTemplate

 

RabbitTemplate是我们在与SpringAMQP整合的时候进行发送消息的关键类
该类提供了丰富的发送消息的方法,包括可靠性消息投递、回调监听消息接口ConfirmCallback、返回值确认接口
ReturnCallback等等同样我们需要注入到Spring容器中,然后直接使用。
 
第一种模型: 一对一模型(直连模型)
 

 

 新建测试类

package com.ztb.test;


import com.ztb.RabbitmqSpringbootApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@SpringBootTest(classes = RabbitmqSpringbootApplication.class)
@RunWith(SpringRunner.class)
public class TestRabbitMQ {
    //注入rabbitTemplet工具
    @Autowired
    private RabbitTemplate rabbitTemplate;
    //写一个helloworld
    @Test
    public void test(){
        rabbitTemplate.convertAndSend("hello","hello  word");
    }
}

 新建消费者类:

package com.ztb.entity;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queuesToDeclare = @Queue("hello"))
//启动队列监听 ,queuesToDeclare  声明队列
public class HelloConsumer {

    @RabbitHandler
    public void recievel(String message){//随便指定一个方法
        System.out.println("message======="+message);
    }
}

测试结果:

 

第二种模型   worke模型(任务模型  多个消费者共同绑定到同一个队列,消费消息) 

新建消费者类:

package com.ztb.work;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.annotation.RabbitListeners;
import org.springframework.stereotype.Component;

@Component
public class WorkCustomer {
    //消费者1
    @RabbitListener(queuesToDeclare = @Queue("work"))
    public void receive1(String message){
        System.out.println("message1"+message);
    }
    //消费者2
    @RabbitListener(queuesToDeclare = @Queue("work"))
    public void  receive2(String message){
        System.out.println("message2"+message);
    }
}

 新建test测试类

 //work模型
    @Test
    public void testWork(){
        for (int i=0;i<10;i++) {
            rabbitTemplate.convertAndSend("work", "work 模型");
        }
    }

 测试结果

 

 第三种:fanout(广播模型) 

 

新建消费者fanoutcustomer类

package com.ztb.fanout;

import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class FanOutCustomer {
@RabbitListener(bindings = {
        @QueueBinding(
                value=@Queue,//创建临时队列
                exchange=@Exchange(value = "logs",type = "fanout")//绑定交换机
        )
})
    public  void recive1(String message){
        System.out.println("message1 = "+message);
    }
    @RabbitListener(bindings = {
            @QueueBinding(
                    value=@Queue,//创建临时队列
                    exchange=@Exchange(value = "logs",type = "fanout")//绑定交换机
            )
    })
    public  void recive2(String message){
        System.out.println("message2 = "+message);
    }
}

新建测试类

//fanout 广播模型
@Test
public void testFanOut(){
        rabbitTemplate.convertAndSend("logs","", "fanout 模型");
}

测试结果:

 

第四种模型:routing 路由模式

Routing 之订阅模型-Direct(直连)

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

在Direct模型下:

  • 队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key)

  • 消息的发送方在 向 Exchange发送消息时,也必须指定消息的 RoutingKey。

  • Exchange不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routingkey与消息的 Routing key完全一致,才会接收到消息

 新建路由消费者:

 

package com.ztb.routing;


import ch.qos.logback.core.boolex.EvaluationException;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;


@Component
public class DirectsCustomer {

    //消费者1
    @RabbitListener(bindings = {
           @QueueBinding(
            value= @Queue,//创建临时队
            exchange = @Exchange(value = "directs",type = "direct"),//自定义交换机类型和名称
            key = {"info","error","warn"}
           )
    })
    public void receive1(String message){
        System.out.println("message1  ="+message);
    }
    //消费者2
    @RabbitListener(bindings = {
            @QueueBinding(
                    value= @Queue,//创建临时队列
                    exchange = @Exchange(value = "directs",type = "direct"),//自定义交换机类型和名称
                    key = {"error"}
            )
    })
    public void receive2(String message){
        System.out.println("message2   ="+message);
    }
}

新建测试类

//routing的模型
@Test
public void testRouting(){
    rabbitTemplate.convertAndSend("directs","info","发送info类型的路由的类的信息");
}

 测试结果:

 

第五种Routing 之订阅模型-Topic

Topic类型的ExchangeDirect相比,都是可以根据RoutingKey把消息路由到不同的队列。只不过Topic类型Exchange可以让队列在绑定Routing key 的时候使用通配符!这种模型Routingkey 一般都是由一个或多个单词组成,多个单词之间以”.”分割,例如: item.insert
# 统配符
        * (star) can substitute for exactly one word.    匹配不多不少恰好1个词
        # (hash) can substitute for zero or more words.  匹配一个或多个词
# 如:
        audit.#    匹配audit.irs.corporate或者 audit.irs 等(#匹配多个)
    audit.*   只能匹配 audit.irs(*匹配一个)

 

新建topic类型的消费者: 

package com.ztb.topic;


import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class TopicCustomer {

    //消费者1
    @RabbitListener(bindings = {
            @QueueBinding(
                    value= @Queue,//创建临时队
                    exchange = @Exchange(name = "topics",type = "topic"),//自定义交换机类型和名称
                    key = {"service.#","product.#","user.*"}
            )
    })

    public void receive1(String message){
        System.out.println("message1  ="+message);
    }
    //消费者2
    @RabbitListener(bindings = {
            @QueueBinding(
                    value= @Queue,//创建临时队列
                    exchange = @Exchange(name= "topics",type = "topic"),//自定义交换机类型和名称
                    key = {"user.save","user.*"}
            )
    })
    public void receive2(String message){
        System.out.println("message2   ="+message);
    }
}
新建测试类
 
//topic   动态路由订阅模型
@Test
public void testTopic(){
    rabbitTemplate.convertAndSend("topics","user.save","user.save 类型的路由的类的信息");
}

 

测试结果
 
 

 

 

总结:
 
 

MQ的应用场景

1,异步处理

场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有两种 1.串行的方式 2.并行的方式
  • 串行方式: 将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。 这有一个问题是,邮件,短信并不是必须的,它只是一个通知,而这种做法让客户端等待没有必要等待的东西.

  • 并行方式:将注册信息写入数据库后,发送邮件的同时,发送短信,以上三个任务完成后,返回给客户端,并行的方式能提高处理的时间。

  • 消息队列:假设三个业务节点分别使用50ms,串行方式使用时间150ms,并行使用时间100ms。虽然并行已经提高的处理时间,但是,前面说过,邮件和短信对我正常的使用网站没有任何影响,客户端没有必要等着其发送完成才显示注册成功,应该是写入数据库后就返回. 消息队列: 引入消息队列后,把发送邮件,短信不是必须的业务逻辑异步处理

 

 由此可以看出,引入消息队列后,用户的响应时间就等于写入数据库的时间+写入消息队列的时间(可以忽略不计),引入消息队列后处理后,响应时间是串行的3倍,是并行的2倍。

 

2,应用解耦

场景:双11是购物狂节,用户下单后,订单系统需要通知库存系统,传统的做法就是订单系统调用库存系统的接口.
 
这种做法有一个缺点:
当库存系统出现故障时,订单就会失败。 订单系统和库存系统高耦合. 引入消息队列
 
 
  • 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
 
  • 库存系统:订阅下单的消息,获取下单消息,进行库操作。 就算库存系统出现故障,消息队列也能保证消息的可靠投递,不会导致消息丢失.

3, 流量削峰

场景: 秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。

作用:

1.可以控制活动人数,超过此一定阀值的订单直接丢弃(我为什么秒杀一次都没有成功过呢^^)

2.可以缓解短时间的高流量压垮应用(应用程序按自己的最大处理能力获取订单)

1.用户的请求,服务器收到之后,首先写入消息队列,加入消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误页面.

2.秒杀业务根据消息队列中的请求信息,再做后续处理.

 

RabbitMQ的集群

1 集群架构

1.1 普通集群(副本集群):只能同步复制exchange ,能同步队列

默认情况下:RabbitMQ代理操作所需的所有数据/状态都将跨所有节点复制。这方面的一个例外是消息队列,默认情况下,消息队列位于一个节点上,尽管它们可以从所有节点看到和访问
  1. 架构图

 

 核心问题:解决了当集群中某一时刻master节点宕机,可以对Quene中信息,进行备份 (主备模式,slave节点无法在主节点宕机之后经行故障转移)

 

 

1.2 镜像集群

 
镜像队列机制就是将队列在三个节点之间设置主从关系,消息会在三个节点之间进行自动同步,且如果其中一个节点不可用,并不会导致消息丢失或服务不可用的情况,提升MQ集群的整体高可用性。(联想redis中的哨兵机制)
  1. 集群架构图

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值