消息中间件RabbitMQ

文档链接:消息队列文档

消息队列面试题(转载):MQ面试题

MQ的定义

RabbitMQ 即一个消息队列,主要是用来实现应用程序的异步和解耦,同时也能起到消息缓冲,消息分发的作用。
 
消息中间件最主要的作用是解耦,中间件最标准的用法是生产者生产消息传送到队列,消费者从队列中拿取消息并处理,生产者不用关心是谁来消费,消费者不用关心谁在生产消息,从而达到解耦的目的。
在分布式的系统中,消息队列也会被用在很多其它的方面,比如:分布式事务的支持,RPC的调用等等。

Rabbitmq系统最核心的组件是Exchange和Queue


消息队列,提供了FIFO的处理机制,具有缓存消息的能力。rabbitmq中,队列消息可以设置为持久化,临时或者自动删除。

MQ功能

1. 流量削峰:高并发访问服务器时,为环节服务器压力,使用消息队列做的一个缓冲。把访问的用户放入MQ进行排队
2.应用解耦:一个系统直接调用其他系统,若其他系统发生故障,则整个流程业务就无法完成,若在两个系统之间加上消息队列,主系统先执行完成后才会发消息给队列,有队列去转达到其他系统,而主系统不收其影响。若期间子系统发生异常,队列分配消息给各个系统,同时队列会监督各子系统完成

3.异步处理:A调用B,B去处理,B要花费较长时间进行处理,A要等待B的处理结果。使用MQ之后,A无需等待B,A可以做自己的事情,等到B完成后发消息给队列MQ,再由MQ发消息给A

三、MQ的分类

1.ActiveMQ:高吞吐量,一般不丢失消息
2.Kafka: 适用于大数据,高吞吐量,日志采集功能。 但单机超过64个队列/分区,队列越多,CPU占用越高
4.RocketMQ: 消息领丢失
5.RabbitMQ: 时效性好
 

四、RabbitMQ

消息中间件,负责接收、存储和转发消息
 

五、四大核心

1. 生产者
2. 交换机
3. 队列
4. 消费者
 

六、六大核心(六大模式)

1. 简单模式

2. 工作模式

3. 发布订阅模式

4. 路由模式

5. 主题模式

6. 发布确认模式

 

七、工作原理

1. Broker : 接收和发布消息的应用,即消息实体

2. Wxchange:交换机(一个消息实体可以对应多交换机)

3. Queue: 队列 (一个交换机可以对应多个队列)

4. Channel :信道,即发消息的通道。 每一个生产者会与MQ之间都会建立一个连接Connection ,每一个链接里会有多个信道

5. Connection : 链接,每次调用MQ都会建立一个链接,链接里有包含多个信道,每次发消息只占用一个信道,而不占用整个连接

6. 绑定:每一个交换机与队列的链接就叫做绑定

 

八、生产者、消费者代码示例

依赖:

<!--指定 jdk 编译版本-->
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>8</source>
                <target>8</target>
            </configuration>
        </plugin>
    </plugins>
</build>
<dependencies>
    <!--rabbitmq 依赖客户端-->
    <dependency>
        <groupId>com.rabbitmq</groupId>
        <artifactId>amqp-client</artifactId>
        <version>5.8.0</version>
    </dependency>
    <!--操作文件流的一个依赖-->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>
</dependencies>

消息生产者:

public class Producer {
    private final static String QUEUE_NAME = "hello";
    public static void main(String[] args) throws Exception {
        //创建一个连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("182.92.234.71");
        factory.setUsername("admin");
        factory.setPassword("123");
        //channel 实现了自动 close 接口 自动关闭 不需要显示关闭
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel()/**
        * 生成一个队列
        * 1.队列名称
        * 2.队列里面的消息是否持久化 默认消息存储在内存中
        * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费
        * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除
        * 5.其他参数
        */
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        String message="hello world";
        /**
        * 发送一个消息
        * 1.发送到那个交换机
        * 2.路由的 key 是哪个
        * 3.其他的参数信息
        * 4.发送消息的消息体
        */
         channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
         System.out.println("消息发送完毕");
        
    }
}

消息消费者

public class Consumer {
    private final static String QUEUE_NAME = "hello";
    public static void main(String[] args) throws Exception{
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("182.92.234.71");
        factory.setUsername("admin");
        factory.setPassword("123");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        System.out.println("等待接收消息.........");
        //推送的消息如何进行消费的接口回调
        DeliverCallback deliverCallback=(consumerTag,delivery)->{String message= new String(delivery.getBody());
                                                                 System.out.println(message);
        };//函数式接口,可使用拉姆达斯表达式
        //取消消费的一个回调接口 如在消费的时候队列被删除掉了
        CancelCallback cancelCallback=(consumerTag)->{
            System.out.println("消息消费被中断");
        };
 
        /**
        * 消费者消费消息
        * 1.消费哪个队列
        * 2.消费成功之后是否要自动应答 true 代表自动应答 false 手动应答
        * 3.消费者未成功消费的回调
        */
        channel.basicConsume(QUEUE_NAME,true,deliverCallback,cancelCallback);
    }
}

九、工作模式

消息生产者向队列发送了大量的消息,消息停留在队列中,无法及时处理,这是需要有很多个消费者(线程)去处理。

注意:这里生产者发送的消息,消费者只能处理一次,即一条消息被工作线程A处理后,线程B不能再处理。 所以消息轮训分发

 

十、消息应答

为了保证消息在发送过程中不丢失,rabbitmq 引入消息应答机制,消息应答就是:消费者在接收到消息并且处理该消息之后,告诉 rabbitmq 它已经处理了,rabbitmq 可以把该消息删除了。 

分类:分为自动应答和手动应答

自动应答:  消息发送后立即被认为已经传送成功,这种模式需要在高吞吐量和数据传输安全性方面做权衡

手动应答:共分为3个方法

A.  Channel.basicAck(用于肯定确认) RabbitMQ 已知道该消息并且成功的处理消息,可以将其丢弃了

B.  Channel.basicNack(用于否定确认)

C.  Channel.basicReject(用于否定确认)

与 Channel.basicNack 相比少一个参数 不处理该消息了直接拒绝,可以将其丢弃了

手动应答又分为批量应答和非批量应答,写代码推荐使用非批量应答,可以避免丢失消息。 但批量应答速度会和快

手动应答的好处是可以批量应答并且减少网络拥堵

multiple 的 true 和 false 代表不同意思
  true 代表批量应答 channel 上未应答的消息
  比如说 channel 上有传送 tag 的消息 5,6,7,8 当前 tag 是8 那么此时
   5-8 的这些还未应答的消息都会被确认收到消息应答
  false 同上面相比
  只会应答 tag=8 的消息 5,6,7 这三个消息依然不会被确认收到消息应答

 

十一、消息的重新入队

如果出现了消息丢失,如何处理呢?

  队列发送2条消息给消费者C1和C2,消息A发送给C1, 消息B发送给C2,在发送过程中,C1突然断开连接,C2正常处理并应答了,此时队列接收不到C1的应答,队列里的消息A暂未删除,队列会将A重新发送给C2进行处理。从而避免消息丢失。 核心思想就是消息自动重新入队

如果消费者由于某些原因失去连接(其通道已关闭,连接已关闭或 TCP 连接丢失),导致消息未发送 ACK 确认,RabbitMQ 将了解到消息未完全处理,并将对其重新排队。如果此时其他消费者可以处理,它将很快将其重新分发给另一个消费者。这样,即使某个消费者偶尔死亡,也可以确保不会丢失任何消息。

 

十二、消息的发布确认

消息的确认分两部分:

  rabbitMQ 确认生产者投递的消息  和  消费者确认 rabbitMQ 服务器的消息(消息应答)

rabbitMQ 确认生产者投递的消息

首先说 RabbitMQ 对生产者的确认,总共分为两种模式分别为 同步模式异步模式

同步模式分为 单条消息确认 与 批量确认。

1. 单条消息确认: channel.waitForConfirms() 普通发送方确认模式;消息到达交换器,就会返回true。

2. 批量消息确认: channel.waitForConfirmsOrDie()批量确认模式;使用同步方式等所有的消息发送之后才会执行后面代码,只要有一个消息未到达交换器就会抛出IOException异常。

异步模式为生产者异步监听消息确认。

异步监听消息确认:channel.addConfirmListener()异步监听发送方确认模式。

消费者对RabbitMQ 消息确认。总共分为两种方式 分别为 手动确认 和 自动确认。 即消费者消费消息后对borker的自动应答和手动应答

 

 

 

 

 

posted @   许君闲乘悦  阅读(138)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示