RabbitMQ

RabbitMQ流程图

比喻:broker相当于数据库服务器,virtual host 相当于库, queue相当于表,queue中的消息相当于表记录。

tcp链接很耗时间,因此一次connection长连接,连接里面有很多channel(信道)

exchange负责接受消息,把消息转到queue存储。exchange起到路由的作用。

没有定义exchange,则默认是direct exchange(直连交换机),会把消息直接转发到queue中。

一个queue可以有一个consumer或多个consumer消费。一个queue中的某一个消息只能被一个consumer消费。

交换机的类型

  1. fanout exchange(扇形交换机):发送给所有绑定的队列,不需要路由建的匹配,相当于广播

  2. direct exchange(直连交换机):根据路由键精确匹配(发送key和binding key一模一样)

  3. topic exchange(主题交换机):通配符匹配(模糊匹配,如:A.* (*:有且只有一个,如:A.B); B.#(#:0个或多个单词,如A.B.C))

  4. header exchange(头部交换机):基于消息体内的header属性进行匹配(非路由)(很少用)

MQ消息的过期策略

两种设置方式(TTL):

  1. 单条消息
  2. 设置队列(整个队列的消息)

死信队列

(DLX:Dead-Letter-Exchange):

过期的消息就变成死信消息(消息过期,队列过期)

队列满了,超出部分的个数,队头(最早)的消息会进入死信

消费者手动确认模式,不确认消息并且不重新投递消息到队列(手动提交:acknowledge-mode: manual)

消费者拒绝消息,并且不重新投递

java 复制代码
 // 正常的队列
    @Bean
    public Queue normalQueue() {
        return QueueBuilder.durable("normalQueue")
                .withArgument("x-message-ttl", 15000)
                .withArgument("x-dead-letter-exchange", dleExchange()) // 设置对应的死信交换机
                .withArgument("x-dead-letter-routing-key", "error") // 设置死信路由key,死信交换机和死信队列绑定的key要一样。否则死信队列可能接受不到消息
                .build();
    }
  
    // 死信交换机(和正常交换机没啥区别)
    @Bean
    public DirectExchange dleExchange() {
        return ExchangeBuilder.directExchange(directName).build();
    }

rabbitMq消费模型的可靠性?

问题:

从rabbitMq流程图观察,三个问题

  1. product推送到broker,可能消息丢失
  2. 发送的routing key不存在,或broker宕机,可能消息丢失
  3. broker到consumer,可能消息丢失

product解决方案

  1. Transaction模式(不推荐)
    类似数据库事务,需要先把一个channel设置成事务模式,通知到broker,broker确认后返回一个ok命令,然后product才发送消息。成功后commit,失败rollback。他是堵塞的。大幅度降低性能的问题
  1. confirm模式(推荐)
    异步确认模式,需要先把一个channel设置成confirm模式,channel发布消息时会为没条消息设置一个ID,broker消费成功后会向product发送一个ack(携带ID),这样product就能知道消息消费成功了。失败的话broker也会想product发送一个nack。

broker解决方案

  1. routing key 不存在的问题
  • 加一个监听器监听不可达的routing key,存储在日志中。或重新推送队列(可能出现死循环)。
  • 加一个备胎交换机(‌alternate Exchange)‌,自动将不可达的routing key转发到备胎交换机的队列中。
  1. 消息未存储丢失的问题
  • 设置消息持久化,队列持久化,交换机持久化
java 复制代码
// 持久化队列
Queue queue = QueueBuilder.durable(QueueName).build();
// 持久化交换机
DirectExchange bossscRedirectExchange = ExchangeBuilder.directExchange(BossSCRedirectRabbitMQConst.EXCHANGE_NAME)
                .durable(true) // 默认持久化
                .build();
// MQ发生消息时候消息持久化
new MessagePostProcessor() {
		public Message postProcessMessage(Message message) throws AmqpException {
            message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
            message.getMessageProperties().setMessageId(messageId);
            message.getMessageProperties().setHeader("Authorization", getToken());
            return message;
      }
 }

RabbitMQ接收到持久化消息之后不会直接持久化,而是写满buffer文件或25ms后再写入磁盘。此时更大程度地保证,可以引入镜像集群保证持久化。

consumer解决方案

  1. 自动提交模式(默认)修改成手动提交模式
yaml 复制代码
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: auto
        ## acknowledge-mode: manual  ## 手动提交
  1. 手动提交模式:(消费者消费时候的手动确认)
java 复制代码
    // 手动确认
    // 参数1:消息的标识,参数2:只确认当前这一条消息,true则批量。消息会认定为消费,MQ接收到后队列删除消息
    // 如果没有及时发送ACK,RabbitMQ会将该消息标记为unacked(未确认)状态,并在消费者channel断开或服务器重启时重新放回队列供其他消费者处理
    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    
    // 手动不确认
    // 参数1:消息的标识,参数2:只确认当前这一条消息,true则批量,参数3:是否重新投递入队这条消息(fasle则拒绝,会进入死信)
    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
    
    // 拒绝消费
    // 参数1:消息的标识,一次只能拒绝一条消息(与手动不确认的区别),参数2:是否重新投递入队这条消息(fasle则拒绝,会进入死信)
    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);

Java开发:

配置:需定义交换机,队列,交换机队列的绑定

集群模式:默认集群模式(每个节点只同步元数据,队列中的数据各个节点存储的消息不同步,节省内存空间,但是当某个节点宕机了,数据就发送接收不了了)

镜像集群模式:节点间同步元数据和所有消息

相关推荐
Freak嵌入式11 分钟前
全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类
java·开发语言·数据结构·python·接口·抽象基类
前端小马21 分钟前
解决IDEA出现:java: 程序包javax.servlet不存在的问题
java·servlet·intellij-idea
IH_LZH1 小时前
Broadcast:Android中实现组件及进程间通信
android·java·android studio·broadcast
去看全世界的云1 小时前
【Android】Handler用法及原理解析
android·java
.Net Core 爱好者1 小时前
Redis实践之缓存:设置缓存过期策略
java·redis·缓存·c#·.net
晚睡早起₍˄·͈༝·͈˄*₎◞ ̑̑1 小时前
苍穹外卖学习笔记(五)
java·笔记·学习
码上一元1 小时前
【百日算法计划】:每日一题,见证成长(017)
java·算法
用生命在耍帅ㅤ1 小时前
java spring boot 动态添加 cron(表达式)任务、动态添加停止单个cron任务
java·开发语言·spring boot
学java的小菜鸟啊1 小时前
第五章 网络编程 TCP/UDP/Socket
java·开发语言·网络·数据结构·网络协议·tcp/ip·udp
zheeez1 小时前
微服务注册中⼼2
java·微服务·nacos·架构