RabbitMQ

⼀、MQ

1.MQ是什么

MQ(Message Queue)消息队列,是基础数据结构中“先进先出”的⼀种数据结构。⼀般⽤来解决应⽤解耦,异步消息,流量削峰等问题,实现⾼性能,⾼可⽤,可伸缩和最终⼀致性架构。把要传输的数据(消息)放在队列中,⽤队列机制来实现消息传递——⽣产者产⽣消息并把消息放⼊队列,然后由消费者去处理。消费者可以到指定队列拉取消息,或者订阅相应的队列,由MQ服务端给其推送消息。

二、RabbitMQ核心

1.1.png
使用docker安装rabbitMQ

docker run -d --name rabbitmq5672 -p 15672:15672 -p 5672:5672 rabbitmq:management

0.png
可视乎访问端口号:15672 其他5672
1.png
账号密码均为guest
RabbitMQ消息中间件,发送和消费消息的软件

1.P2P消息

普通消息,⼀对⼀,⼀个队列有⼀个发送端和⼀个消费端。
最常⻅的,其中发送端和消费端互不影响
1.依赖jar

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

2.配置application.yml

spring:
  rabbitmq:
    host: 47.92.168.143
    port: 5672        #端口号
    username: guest
    password: guest

3.创建队列

@Configuration //spring配置注解
public class RabbitMQConfig {
    //创造队列 P2P
    @Bean
    public Queue createQueue1(){
       return new Queue("k-P2P");//队列名称 唯一
       }
   }

4.编写发送消息的代码

@SpringBootTest
class RabbitMqApplicationTests {
    @Resource
    private RabbitTemplate rabbitTemplate;//ioc自动创建
    //P2P
    @Test
    void contextLoads() {
        //发送消息 参数说明:1.交换器 2.队列名或路由名 3.消息内容
       rabbitTemplate.convertAndSend("","k-P2P","测试rabbit-P2P");
    }
}

5.消费者接收

@Component
@RabbitListener(queues = "k-P2P" ) //对应队列名称
public class QueueListener {
   private Logger logger= LoggerFactory.getLogger(QueueListener.class);
   //接受消息
   @RabbitHandler    //参数为发送的类型
   public void listener(String msg){
       logger.error(msg);
   }
}

2.png
3.png
接收成功
4.png

1.1原生的发送

public class RabbitUtil {
    //1.连接服务器
    //1.1 创建连接工厂 设计模式:工厂模式
   private static ConnectionFactory factory=new ConnectionFactory();

    /**手动连接并发送消息*/
    public static void RabbitMQTransport(String queue,String value) throws IOException, TimeoutException {
        factory.setHost("47.92.168.143");
        factory.setPort(5672);
        factory.setUsername("guest");
        factory.setPassword("guest");
        //1.2 创建连接对象
        Connection connection= factory.newConnection();
        //1.3 创建通道对象
        Channel channel=connection.createChannel();
        //2.创建队列
        /**
         * 参数说明:
         * 1.队列名
         * 2.是否持久化
         * 3.是否为排他队列 一次性
         * 4.是否自动删除 */
        channel.queueDeclare(queue,false,false,false,null);
        //3.发送消息
        channel.basicPublish("",queue,null,value.getBytes());
        //4.关闭释放
        channel.close();
        connection.close();
    }
}

1.2原生的接收

@Component
@RabbitListener(queues = "k-P2PTwo" ) //对应队列名称
public class QueueListenerTwo {

   private Logger logger= LoggerFactory.getLogger(QueueListenerTwo.class);

   @RabbitHandler    //参数类型为返回类型
   public void listener(Object msg){
       //验证
       if (msg instanceof Message){
           Message message= (Message) msg;
           logger.error("原生接受消息队列:"+new String(message.getBody()));
       }else {
           logger.error("原生接受消息队列:"+msg);
       }
   }
}

先执行发送-创建-接收(若未创建队列-写接收则会报错 找不到队列)

2.Work消息

⼀个队列可以有多个消费者,这些消费者之间
如果存在发送的快,消费的慢,这样就会消息堆积,需要为队列多加点消费者
代码实现:
1.创建队列

//创造队列 Work
    @Bean
    public Queue createQueue2(){
       return new Queue("k-Work");
    }

2.编写发送消息的代码

//Word ⼀个队列可以有多个消费者 数据丢失
@Test
void  t1(){
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend("", "k-Work", "测试rabbit-Word" + i);
        }

3.配置两个消费者-监听器

@Component
@RabbitListener(queues = "k-Work")
public class QueueListenerWork {
   private Logger logger= LoggerFactory.getLogger(QueueListenerWork.class);
   @RabbitHandler  //参数类型为返回类型
   public void  listener(String msg){
       logger.error("消费者1:Word"+msg);
   }
}
@Component
@RabbitListener(queues = "k-Work")
public class QueueListenerWorkTwo {
    private Logger logger= LoggerFactory.getLogger(QueueListenerWork.class);
    @RabbitHandler  //参数类型为返回类型
    public void  listener(String msg){
        logger.error("消费者2:Word"+msg);
    }
}

结果:
5.png
扩展 使用SpringBootTest时会启动springboot 这时springboot相当于另一个一样的消费者进行强夺
6.png
这些消费者之间是竞争关系,最终⼀个消息只能被消费⼀次

3.Pub/Sub消息

发布订阅消息,基于Exchange实现的消息可以被发送到多个队列中。其中交换器有四种类型:
fanout、direct、topic、header(基本上不⽤)。⼀般⽤来实现⼀个消息需要发送到多个队列中。消息同
时被多个消费

3.1fanout类型

Exchange的常⽤的交换器类型,第⼀种:fanout 直接转发
7.png
代码实现:
1.创建所需的队列、交换器、绑定

@Configuration
public class RabbitMQFanout {
    //1.创建队列
    @Bean
    public Queue createexQ1(){
        return new Queue("kz-fanout-1");
    }
    @Bean
    public Queue createexQ2(){
        return new Queue("kz-fanout-2");
    }
    //2.创建交换器 IOC创建容器
    @Bean
    public FanoutExchange createFe(){
        return new FanoutExchange("kz-ex-fanout");
    }
    //3.创建绑定关系
    @Bean
    public Binding createBe1(FanoutExchange fe){
        return BindingBuilder.bind(createexQ1()).to(fe);
    }
    @Bean
    public Binding createBe2(FanoutExchange fe){
        return BindingBuilder.bind(createexQ2()).to(fe);
    }
}

2.发送

// Exchange交换器 类型 通过fanout交换器 实现消息发送
@Test
public void t3(){
    //发送消息 参数说明:1.交换器 2.队列名或路由名 3.消息内容
    rabbitTemplate.convertAndSend("kz-ex-fanout", "", "通过交换器来发送!");
}

8.png
查看
7.1.png

3.2direct类型

9.png
direct类型,发送消息到交换器,交换器根据消息的路由匹配内容,进⾏匹配队列,最终把把消息发送
给匹配到所有队列
根据名称进行匹配转发
11.png
向交换器发送消息
10.png
只有error队列可以接受到转发的消息

3.3topic类型

12.png
topic类型,⽀持路由匹配,其中路由内容⽀持模糊匹配,特殊符号

* ⼀个单词,通过.区分单词
# 0或多个单词,通过.区分单词

14.png
进行发送
15.png
查看
13.png

4.RabbitMQ基于死信实现延迟

死信:RabbitMQ的队列中的消息,满⾜以下条件任意其⼀
1.消息被拒绝
2.消息过期
3.队列已满
死信交换器:专⻔⽤来转发死信消息。
死信队列:通过死信交换器发送过来的消息,存储的都是死信消息
延迟队列:可以实现延迟消息处理的队列。⼀般把具备有效期的队列并且设置的有死信交换器的队列并且没有消费者成为延迟队列
1.订单超时⾃处理
⽐如
外卖订单15分钟不付款,⾃动取消
12306订单30分钟不付款,⾃动取消
延迟消息处理,5分钟后处理
⾃动确认收货
17.png
1.创建延迟队列

/**延迟队列 生成死信 */
public static final String DL_ONE_NAME="kz-dl-001";
/**死信队列 接收死信消息 .*/
public static final String DL_TWO_NAME="kz-dl-002";
/**死信转换器 */
public static final String DL_EXCHANGE_NAME="kz-dl-exchange";
/**死信路由转换*/
public static final String DL_RK="kz-dl-rk";

2.延迟队列创建方式

/**延迟队列*/
@Bean
public Queue createDl1(){
    //1.创建队列属性信息
    Map<String, Object> params = new HashMap<>();
    //设置队列中每个消息的有效期     毫秒
    params.put("x-message-ttl",10000);
    //设置对应的死信交换器
    params.put("x-dead-letter-exchange",DL_EXCHANGE_NAME);
    //设置交换器匹配的路由名称 (队列或交换器)
    params.put("x-dead-letter-routing-key",DL_RK);
    //2.创建延迟队列                             设置队列信息
    return QueueBuilder.durable(DL_ONE_NAME).withArguments(params).build();
}

设置队列中每个消息的有效期
设置对应的死信交换器
设置交换器匹配的路由名称 (队列或交换器)
创建延迟队列

3.绑定

/**死信接收队列*/
@Bean
public Queue createDl2(){
    return new Queue(DL_TWO_NAME);
}

/**死信交换器 路由匹配*/
@Bean
public DirectExchange createDLEc(){
    return new DirectExchange(DL_EXCHANGE_NAME);
}

/**实现绑定*/
@Bean
public Binding createDlBe(DirectExchange de){
                      //     死信接收队列     死信交换器    死信路由匹配
    return BindingBuilder.bind(createDl2()).to(de).with(DL_RK);
}

18.png
4.发送消息

@RestController
@RequestMapping("/api/rabbit/")
public class RabbitDlController {
    @Resource
    private RabbitTemplate rabbitTemplate;//ioc自动创建
    private Logger logger= LoggerFactory.getLogger(RabbitDlController.class);

    @GetMapping("save.do")
    public String save(String msg){
        logger.warn("发送消息" +msg+System.currentTimeMillis());     //              死信队列
        rabbitTemplate.convertAndSend("", RabbitDlConfig.DL_ONE_NAME,msg);
        return "OK";
    }
      //路由传参
    @GetMapping("{msg}")
    public String save2(@PathVariable String msg){
        logger.warn("发送消息" +msg+System.currentTimeMillis());     //              死信队列
        rabbitTemplate.convertAndSend("", RabbitDlConfig.DL_ONE_NAME,msg);
        return "OK";
    }
}

6.接收队列-消费者

@Component
@RabbitListener(queues = RabbitDlConfig.DL_TWO_NAME)
public class RabbitDlListener {

    private Logger logger= LoggerFactory.getLogger(RabbitDlListener.class);

    @RabbitHandler
    public void dlListener(String msg){
        logger.warn("死信接受延迟队列:"+msg+System.currentTimeMillis());
    }
}

19.png
设置了有效期为10秒、10秒之内没有消费者接收、转到死信交换器、根据绑定转发到指定的队列

4.1RabbitMQ事务

我们需要⼀次性发多条消息,需要开启事务,事务⽤来保证多条消息的发送,如果有异常出
现,消息都不发送
实现步骤:
1.创建RabbitMQ事务管理

//2.创建RabbitMQ管理事务
@Bean
public RabbitTransactionManager create(ConnectionFactory factory){
    return new RabbitTransactionManager(factory);
}

2.在发送时开启事务

/**加事务*/    //回滚            异常类型
@Transactional(rollbackFor = Exception.class)
@GetMapping("save.do")
public String save(String msg){
    //开启事务
    rabbitTemplate.setChannelTransacted(true);
    logger.warn("发送消息:" +msg+System.currentTimeMillis());
    rabbitTemplate.convertAndSend("", RabbitMQTranConfig.TRAN,msg);
    System.err.println(1/0);
    return "OK";
}

20.png

4.2RabbitMQ手动应答

RabbitMQ防⽌消息的重复消费或者消息丢失的时候,都可以使⽤⼿动应答代替⾃动应答
针对消费端 在消费者代码实现
实现步骤:

@Component                                          //设置手动
@RabbitListener(queues = RabbitAckConfig.QUEUE_ACK,ackMode = "MANUAL")
public class RabbitAckListener {
    private Logger logger= LoggerFactory.getLogger(RabbitAckListener.class);

    @RabbitHandler           //channel可以理解为一个先进先出的消息队列     消息内部唯一id
    public void ackListener(String msg, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
        //手动操作      //1.消息id   2.是否应答 true 成功 false失败  3.消息是否还添加到队列
        channel.basicNack(tag,false,true);
        logger.warn("手动应答:"+msg);
    }
}
    //自动
    //        channel.basicAck(tag,true);

21.png
只要第三个参数为true就会将消息添加到队列,造成死循环

4.1RabbitMQ如何防止消息丢失

1.发送端如何保证消息不丢失
事务机制和Confirm机制,注意:事务机制和 Confirm 机制是互斥的,两者不能共存,会导致
RabbitMQ 报错。
2.RabbitMQ服务器端如何保证消息不丢失
持久化、集群、普通模式、镜像模式。
3.消费端如何保证消息不丢失
basicAck机制、死信队列、消息补偿机制

4.2RabbitMQ如何保证消息的幂等性

在编程中⼀个幂等操作的特点是其任意多次执⾏所产⽣的结果与⼀次执⾏的产⽣的结果相同,在mq中
由于⽹络故障或客户端延迟消费mq⾃动重试过程中可能会导致消息的重复消费,那我们如何保证消息
的幂等问题
1、⽣成全局id(雪花算法),存⼊redis或者数据库,在消费者消费消息之前,查询⼀下该消息是否有消费过。
2、如果该消息已经消费过,则告诉mq消息已经消费,将该消息丢弃(⼿动ack)。
3、如果没有消费过,将该消息进⾏消费并将消费记录写进redis或者数据库中。

4.3RabbitMQ集群模式

RabbitMQ 有三种模式: 单机模式 , 普通集群模式 , 镜像集群模式 。
单机模式:就是demo级别的,⼀般就是你本地启动了玩玩⼉的,没⼈⽣产⽤单机模式
普通集群模式:意思就是在多台机器上启动多个RabbitMQ实例,每个机器启动⼀个。
镜像集群模式:这种模式,才是所谓的RabbitMQ的⾼可⽤模式,跟普通集群模式不⼀样的是,你创建
的queue,⽆论元数据(元数据指RabbitMQ的配置数据)还是queue⾥的消息都会存在于多个实例上,然后每次你写消息到queue的时候,都会⾃动把消息到多个实例的queue⾥进⾏消息同步

总结

RabbitMQ是MQ的⼀种,消息队列或消息中间件,实现消息的异步处理,降低耦合度,提⾼性能
RabbitMQ安装-Docker
RabbitMQ的消息
1.消息⼀对⼀ 发送消息直接到队列,从队列中进⾏消费
2.消息⼀对多 发送消息到了交换器(
4种),从交换器到队列中,从队列中进⾏消费
**RabbitMQ交换器 **
1.fanout类型直接转发
2.direct类型对消息过滤,路由匹配,精确
3.topic类型对消息过滤,路由匹配,模糊 * #
4.header类型 对消息过滤,指定的消息头
**RabbitMQ死信和延迟 **
1.死信:当消息满⾜以下条件之⼀:1.队列拒绝 2.队列满了 3.过期
2.延迟:借助死信机制,如果⼀个队列⽣成了死信(时间过期),可以⾃动传递死信交换器,可以
把死信消息发送到要处理的队列
RabbitMQ的事务
保证多个消息要么都成功,要么都失败
RabbitMQ的⼿动应答
防⽌消息丢失,可以在消费端开启⼿动处理

### RabbitMQ 使用指南和教程 #### 安装 RabbitMQ RabbitMQ 是一种基于 AMQP 协议的消息中间件,用于实现分布式系统的可靠消息传递。以下是安装 RabbitMQ 的基本流程: 1. **安装 Erlang** RabbitMQ 基于 Erlang 编程语言开发,因此需要先安装 Erlang 运行环境。可以通过包管理器或者下载官方二进制文件完成安装。 2. **安装 RabbitMQ Server** 下载并安装 RabbitMQ Server 软件包。对于 Linux 用户,可以使用以下命令: ```bash sudo apt-get install rabbitmq-server ``` 3. **启动服务** 启动 RabbitMQ 服务后,默认监听端口为 `5672`(AMQP 协议),Web 管理界面默认运行在 `15672` 端口上。 ```bash sudo systemctl start rabbitmq-server ``` 4. **启用 Web 管理插件** 可通过以下命令启用 RabbitMQ 提供的 Web 管理工具: ```bash sudo rabbitmq-plugins enable rabbitmq_management ``` --- #### 配置用户与权限 为了安全访问 RabbitMQ 实例,通常需要创建自定义用户并分配相应权限。 - 创建新用户: ```bash rabbitmqctl add_user rabbitmq 211314 ``` 此操作会新增名为 `rabbitmq` 的用户,并将其密码设为 `211314`[^1]。 - 设置用户角色: ```bash rabbitmqctl set_user_tags rabbitmq administrator ``` 将该用户的标签设定为管理员角色,使其拥有完全控制权[^1]。 - 授予用户权限: ```bash rabbitmqctl set_permissions -p "/" rabbitmq ".*" ".*" ".*" ``` 上述命令授予用户对根虚拟主机 `/` 中所有资源的操作权限[^1]。 - 查看现有用户及其角色: ```bash rabbitmqctl list_users ``` --- #### 集群配置 RabbitMQ 支持多种集群模式来提升可用性和性能。主要分为两类:普通模式和镜像模式。 - **普通模式** 在这种模式下,各节点独立存储队列中的数据和其他元信息(如交换机)。当客户端尝试消费某个不在当前连接节点上的消息时,目标节点会被请求转发所需的数据[^2]。 - **镜像模式** 对比之下,在镜像模式中,指定队列的内容将在多个节点间保持一致副本。即使部分成员失效,剩余存活节点仍能继续提供完整的服务功能[^2]。 > 注意事项:尽管镜像模式提高了可靠性,但也带来了额外开销——网络流量增加以及写入延迟上升等问题需被充分考虑进去。 --- #### 应用集成示例 假设要在一个 Java 或 Python 应用程序里利用 RabbitMQ 来发送/接收消息,则可能涉及以下几个步骤: 1. **声明交换器 (Exchange)** 和绑定关系: ```java channel.exchangeDeclare(exchangeName, "direct", true); channel.queueDeclare(queueName, true, false, false, null); channel.queueBind(queueName, exchangeName, routingKey); ``` 如此一来便完成了持久化队列及路由键关联工作[^3]。 2. 发布一条测试消息至上述已建立好的通道路径之中; 3. 订阅对应主题下的事件流以便实时捕获最新动态更新情况; --- #### 总结 以上涵盖了从基础安装到高级特性使用的整个过程概述。希望这些指导能够帮助您快速掌握如何部署与维护属于自己的 RabbitMQ 平台实例! 问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kkkouz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值