RabbitMQ总结

一. RabbitMQ介绍

1. 什么是MQ?

消息队列(Message Queue,简称MQ):是在消息的传输过程中保存消息的容器。用于分布式系统之间进行通信。

RabbitMQ总结_第1张图片

 2.  选型和对比

RabbitMQ总结_第2张图片

 3.什么是RabbitMQ?

        RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。

        AMQP,即 Advanced Message Queuing Protocol(高级消息队列协议),是一个网络协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。2006年,AMQP规范发布。类比HTTP。

        2007年,Rabbit技术公司基于AMQP标准开发的RabbitMQ1.0发布。RabbitMQ采用Erlang 语言开发。

RabbitMQ总结_第3张图片

 专业名词说明:

Broker(消息代理):接收和分发消息的应用, RabbitMQ Server 就是 Message Broker。

Virtual host:出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念。当多个不同的用户使用同一个 RabbitMQ server 提供的服务时,可以划分出多个 vhost,每个用户在自己的 vhost 创建 exchange/ queue 等。
Connection: publisher/ consumer 和 broker 之间的 TCP 连接。

Channel(通道):如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection 的开销将是巨大的,效率也较低。 Channel 是在 connection 内部建立的逻辑连接,如果应用程序支持多线程,通常每个 thread 创建单独的 channel 进行通讯, AMQP method 包含了 channel id 帮助客户端和 message broker 识别 channel,所以 channel 之间是完全隔离的。 Channel 作为轻量级的Connection 极大减少了操作系统建立 TCP connection 的开销。
Exchange(交换机): message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到 queue 中去。常用的类型有: direct (point-to-point), topic (publish-subscribe) and fanout (multicast).

Queue: 消息最终被送到这里等待 consumer 取走, 消费后被移除队列。

Binding: exchange 和 queue 之间的虚拟连接, binding 中可以包含 routing key, Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据。

4.为什么要使用RabbitMQ?

1.解耦

传统模式:系统间耦合性太强,如图所示,系统A在代码中直接调用系统B和系统C的代码,如果将来D系统接入,系统A还需要修改代码,过于麻烦!

RabbitMQ总结_第4张图片

中间件模式:将消息写入消息队列,需要消息的系统自己从消息队列中订阅,从而系统A不需要做任何修改。

RabbitMQ总结_第5张图片

2.异步 

传统模式: 一些非必要的业务逻辑以同步的方式运行,太耗费时间.

RabbitMQ总结_第6张图片

中间件模式: 将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度 .

RabbitMQ总结_第7张图片

3.削峰 

传统模式:并发量大的时候,所有的请求直接怼到数据库,造成数据库连接异常.

RabbitMQ总结_第8张图片

中间件模式: 系统A慢慢的按照数据库能处理的并发量,从消息队列中慢慢拉取消息 .

RabbitMQ总结_第9张图片

5.RabbitMQ用到的启动器 


    org.springframework.boot
    spring-boot-starter-amqp

二. RabbitMQ的五种消息模型

RabbitMQ提供了6种消息模型,但是第6种其实是RPC,并不是MQ。那么也就剩下5种。

但是其实3、4、5这三种都属于订阅模型,只不过进行路由的方式不同。

RabbitMQ总结_第10张图片

1.simple-简单模型 

RabbitMQ是一个消息代理:它接受和转发消息。 你可以把它想象成一个邮政信箱. 

RabbitMQ与邮局的主要区别是它不处理纸张,而是接受,存储和转发数据消息的二进制数据块。

RabbitMQ总结_第11张图片

 P(producer/ publisher):生产者,一个发送消息的用户应用程序。

C(consumer):消费者,消费和接收有类似的意思,消费者是一个主要用来等待接收消息的用户应用程序.

队列(红色区域):rabbitmq内部类似于邮箱的一个概念。虽然消息流经rabbitmq和你的应用程序,但是它们只能存储在队列中。队列只受主机的内存和磁盘限制,实质上是一个大的消息缓冲区。许多生产者可以发送消息到一个队列,许多消费者可以尝试从一个队列接收数据。

总之:

   生产者将消息发送到队列,消费者从队列中获取消息,队列是存储消息的缓冲区。

我们将用Java编写两个程序;发送单个消息的生产者,以及接收消息并将其打印出来的消费者。我们将详细介绍Java API中的一些细节,这是一个消息传递的“Hello World”。

我们将调用我们的消息发布者(发送者)Send和我们的消息消费者(接收者)Recv。发布者将连接到RabbitMQ,发送一条消息,然后退出。

生产者发送消息

//发送消息的生产者
public class Sender {
    //定义消息队列的名称
    private static final String QUEUE_NAME="simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建连接中的通道,轻量级的connection, 大部分的api都在这里完成
        Channel channel = connection.createChannel();
        //声明一个消息队列,必须声明消息队列才能发送消息,我们可以把消息发送到消息队列中
        //只有当他不存在时才会被创建
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //消息内容
        String msg = "Hello World!";
        //通道发送消息(二进制数据块)到消息队列中
        channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());
        System.out.println("生产者发送消息:"+msg);

        //关闭通道和连接
        channel.close();
        connection.close();
    }
}

通过管理工具查看消息

RabbitMQ总结_第12张图片

此时消息队列中就存在了一条消息. 在没有消费者消费消息之前, 消息都会存在消息队列中.

消费者获取消息 

//消费者消费消息
public class Recv {
    //消息队列的名称
    private static final String QUEUE_NAME="simple_queue";

    public static void main(String[] args) throws IOException, TimeoutException {
        //获取连接
        Connection connection = ConnectionUtil.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //定义队列的消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            //获取消息,并且处理,这个方法类似于事件监听,如果有消息的时候会被自动调用
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                //body就是消息体
                String msg = new String(body);
                System.out.println("消费者接收消息:" + msg);
                /**
                 * 手动ack, 进行消息确认
                 * getDeliveryTag:该消息的index,表示消息队列中第几条数据
                 * multiple: 是否处理多条
                 */
                channel.basicAck(envelope.getDeliveryTag(),false);
                System.out.println("DeliveryTag:"+envelope.getDeliveryTag());
            }
        };

        //监听的队列, 第二个参数: 是否自动确认
        channel.basicConsume(QUEUE_NAME,false,consumer);
    }
}

控制台

RabbitMQ总结_第13张图片

 这个时候,队列中的消息就没了:

RabbitMQ总结_第14张图片

 我们发现,消费者已经获取了消息,但是程序没有停止,一直在监听队列中是否有新的消息。一旦有新的消息进入队列,就会立即打印. 消息一旦被消费者接收,队列中的消息就会被删除。

问题:  如何保证消费者把消息成功消费?

        如果消费者领取消息后,  还没执行操作就挂掉了呢? 或者抛出了异常? 消息消费失败,  但是RabbitMQ无从得知,  这样消息就丢失了!         

        因此,RabbitMQ有一个ACK机制(消息确认机制)。当消费者获取消息后,会向RabbitMQ发送回执ACK,告知消息已经被接收。不过这种回执ACK分两种情况:

  • 自动ACK:消息一旦被接收,消费者自动发送ACK

  • 手动ACK:消息接收后,不会发送ACK,需要手动调用

那么觉得哪种更好呢?

这需要看消息的重要性:

  • 如果消息不太重要,丢失也没有影响,那么自动ACK会比较方便

  • 如果消息非常重要,不容丢失。那么最好在消费完成后手动ACK,否则接收消息后就自动ACK,RabbitMQ就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。

你可能感兴趣的:(java-rabbitmq,rabbitmq,java)