初识RabbitMQ_Java连接RabbitMQ(附源码)

初识RabbitMQ_Java连接RabbitMQ(附源码)

引言:

       本文主要分享有关RabbitMQ的相关知识,包括:RabbitMQ的简介、利用main进行演示各种情况下的消息队列、非持久化的简单案例、工作队列、公平的分发消息、实现持久化队列的方法;

文章目录

  • 初识RabbitMQ_Java连接RabbitMQ(附源码)
  • 1. RabbitMQ简介
    • 1.1 AMQP
    • 1.2 RabbitMQ环境的安装
  • 2. 环境的搭建
      • 导入依赖
  • 2. 非持久化队列的接收和发送
    • 2.1 发送消息
    • 2.2 接收消息
  • 3. 工作队列
    • 3.1 消息发送
    • 3.2 接受消息(两个)
  • 4. 消息队列的公平分发
    • 4.1 发送消息
    • 4.2 接受消息(两个)
  • 5. 消息队列中的持久化
    • 5.1 发送消息
    • 5.2 接收消息

1. RabbitMQ简介

         rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统,遵循Mozilla Public License开源协议,服务器端用Erlang语言编写,支持多种客户端的工业级的消息队列(MQ)服务器,RabbitMQ 是建立在Erlang OTP平台上;

1.1 AMQP

AMQP(Advanced Message Queuing Protocol),高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。

​ AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。

AMQP协议本身包含三层:

  • Model Layer:位于协议最高层,主要定义了一些供客户端调用的命令,客户端可以通过这些命令实现自己的业务逻辑,例如,客户端可以通过queue declare声明一个队列,利用consume命令获取队列的消息;

  • Session Layer:主要负责将客户端命令发送给服务器,在将服务器端的应答返回给客户端,主要为客户端与服务器之间通信提供可靠性、同步机制和错误处理;

  • Transport Layer:主要传输二进制数据流,提供帧的处理、信道复用、错误检测和数据表示;

1.2 RabbitMQ环境的安装

RabbitMQ的安装环境配置:https://blog.csdn.net/weixin_42601136/article/details/108672763

2. 环境的搭建

创建SpringBoot项目,加入web和lombook;

导入依赖

<dependency>
    <groupId>com.rabbitmqgroupId>
    <artifactId>amqp-clientartifactId>
dependency>

2. 非持久化队列的接收和发送

简单的入门案例

  • P: producer 生产者
  • C: consumer 消费者
  • 中间红色的部分是一个队列,在RabbitMQ中代表消息缓冲区

在这里插入图片描述

2.1 发送消息

创建一个生产者,默认非持久化,这里发送了两次

六步:

  1. 创建连接工厂
  2. 创建连接
  3. 创建信道
  4. 创建消息
  5. 发送消息
  6. 释放资源
/**
 * 生产者,消息发送者
 * Created by Kak on 2020/9/21.
 */

public class ProducerMessage {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //创建消息
        String msg = "This is rabbitMQ message!!!";
        //发送消息
        channel.basicPublish("","hello",null,msg.getBytes());
        //释放资源
        channel.close();
        connection.close();
    }
}

在这里插入图片描述

初识RabbitMQ_Java连接RabbitMQ(附源码)_第1张图片

2.2 接收消息

从队列中获取消息

五步:

  1. 创建连接工厂
  2. 创建连接
  3. 创建信道
  4. 消费队列里面的消息
  5. 释放资源
/**
 * 消息接受者
 * Created by Kak on 2020/9/21.
 */
public class ConsumerMessage {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        System.out.println("准备接收消息!!!");
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("consumer--------" + new String(body, "UTF-8"));
            }
        };
        /**
         * 1:侦听的队列
         * 2:是否自动提醒
         * 3:接收消息后回调执行的逻辑
         */
        channel.basicConsume("hello", true, consumer);
        //使用标准输入中断
        System.in.read();
        channel.close();
        connection.close();
    }
}

初识RabbitMQ_Java连接RabbitMQ(附源码)_第2张图片

发送成功!!!

在这里插入图片描述

3. 工作队列

看似公平实则不公平,两个收消息的一人一个,忽略了每个接受端的工作效率;

初识RabbitMQ_Java连接RabbitMQ(附源码)_第3张图片

3.1 消息发送

/**
 * 生产者,消息发送者
 * Created by Kak on 2020/9/21.
 */

public class ProducerMessage {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        for(int i = 0; i<10;i++){


        //创建消息
        String msg = "This is rabbitMQ message!!!"+i;
        //发送消息
        channel.basicPublish("","hello",null,msg.getBytes());
            System.out.println("------producer------:"+msg);
        }
        //释放资源
        channel.close();
        connection.close();
    }
}

3.2 接受消息(两个)

/**
 * 消息接受者
 * Created by Kak on 2020/9/21.
 */
public class ConsumerMessage_01 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        System.out.println("准备接收消息!!!");
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("consumer1--------" + new String(body, "UTF-8"));
            }
        };
        /**
         * 1:侦听的队列
         * 2:是否自动提醒
         * 3:接收消息后回调执行的逻辑
         */
        channel.basicConsume("hello", true, consumer);
        //使用标准输入中断
        System.in.read();
        channel.close();
        connection.close();
    }
}
/**
 * 消息接受者
 * Created by Kak on 2020/9/21.
 */
public class ConsumerMessage_02 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        System.out.println("准备接收消息!!!");
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("consumer2--------" + new String(body, "UTF-8"));
            }
        };
        /**
         * 1:侦听的队列
         * 2:是否自动提醒
         * 3:接收消息后回调执行的逻辑
         */
        channel.basicConsume("hello", true, consumer);
        //使用标准输入中断
        System.in.read();
        channel.close();
        connection.close();
    }
}

初识RabbitMQ_Java连接RabbitMQ(附源码)_第4张图片
初识RabbitMQ_Java连接RabbitMQ(附源码)_第5张图片

初识RabbitMQ_Java连接RabbitMQ(附源码)_第6张图片

  • 发布者发布的消息会平均分配到每一个消费者,采用默认的任务分发机制(轮询),这种任务队列的优点之一就是可以轻易的并行工作,如果我们积压很多任务,我们可以通过增加工作者(consumer)来解决这一问题,使得系统的伸缩性更强。但是这种分发机制没有考虑处理处理任务的时间问题(因为他分配任务的时候是一次性分配,并非是一个一个分配),按照轮询的方式将任务等分给了两个消费者,可能某一个消费者性能比较差,累积任务会越来越多,所以一直忙个不停;而另一个消费者性能比较好,处理任务块,可能闲的不行,这就造成了资源浪费;

4. 消息队列的公平分发

轮询机制会造成资源浪费的问题,原因是因为RabbitMQ在分发任务的时候盲目的一次性平均分配任务,它分发给消费者的任务后,不看消费者是否完成,直接在分配就造成了累积;

通过限制RabbitMQ只发不超过1条的消息给同一个消费者,且当消息处理完毕后,有了反馈,才会进行第二次发送;

int prefetchCount = 1;
channel.basicQos(prefetchCount);

当然在使用basicQos方法的时候还需要设置autoAck: 设置应答模式,默认是自动应答

  • 自动应答:只要消息从队列中获取,无论消费者获取到消息后是否成功消息,都认为是消息已经成功消费
  • 手动应答:消费者从队列中获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,如果消费者一直没有反馈,那么该消息将一直处于不可用状态,打开手动确认还有一个好处就是当消费者不正常死亡,RabbitMQ会将这个消息进行重新加入队列进行排队

初识RabbitMQ_Java连接RabbitMQ(附源码)_第7张图片

4.1 发送消息

/**
 * 生产者,消息发送者
 * Created by Kak on 2020/9/21.
 */

public class ProducerMessage {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //确保同时只发送1个消息
        channel.basicQos(1);
        for(int i = 0; i<10;i++){
        //创建消息
        String msg = "This is rabbitMQ message!!!"+i;
        //发送消息
        channel.basicPublish("","hello",null,msg.getBytes());
            System.out.println("------producer------:"+msg);
        }
        //释放资源
        channel.close();
        connection.close();
    }
}

4.2 接受消息(两个)

将1接收者设置睡眠时间,模拟低效率;

/**
 * 消息接受者
 * Created by Kak on 2020/9/21.
 */
public class ConsumerMessage_01 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        System.out.println("准备接收消息!!!");
        channel.basicQos(1);
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String s, Delivery delivery) throws IOException {
                System.out.println("consumcrl:-----" + new String(delivery.getBody(), "UTF-8"));
                try {
                    Thread.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 手动通知
                channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
            }
        };

        /**
         * 1:侦听的队列
         * 2:是否自动提醒
         * 3:接收消息后回调执行的逻辑
         * 4:取消或者异常消费时,执行的回调逻辑
         */
        channel.basicConsume("hello", false, deliverCallback, new CancelCallback() {
            @Override
            public void handle(String s) throws IOException {

            }
        });
        //使用标准输入中断
        System.in.read();
        channel.close();
        connection.close();
    }
}
/**
 * 消息接受者
 * Created by Kak on 2020/9/21.
 */
public class ConsumerMessage_02 {
    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        System.out.println("准备接收消息!!!");
        channel.basicQos(1);
        DeliverCallback deliverCallback = new DeliverCallback() {
            @Override
            public void handle(String s, Delivery delivery) throws IOException {
                System.out.println("consumcr2:-----" + new String(delivery.getBody(), "UTF-8"));
                // 手动通知
                channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
            }
        };

        /**
         * 1:侦听的队列
         * 2:是否自动提醒
         * 3:接收消息后回调执行的逻辑
         * 4:取消或者异常消费时,执行的回调逻辑
         */
        channel.basicConsume("hello", false, deliverCallback, new CancelCallback() {
            @Override
            public void handle(String s) throws IOException {

            }
        });
        //使用标准输入中断
        System.in.read();
        channel.close();
        connection.close();
    }
}

初识RabbitMQ_Java连接RabbitMQ(附源码)_第8张图片

初识RabbitMQ_Java连接RabbitMQ(附源码)_第9张图片

初识RabbitMQ_Java连接RabbitMQ(附源码)_第10张图片

5. 消息队列中的持久化

上面说的队列在RabbitMQ退出或者崩溃时就会消失的队列,如果当时队列里面还有消息未被消费,那么异常退出会造成任务丢失;为了防止RabbitMQ意外退出造成队列中的任务丢失就用到了消息队列的持久化;

设置持久化队列需要在提供者和消费者中设置:

channel.queueDeclare(QUEUE_NAME,true,false,false,null); //第二个参数设置为true

当我们消息发布者发布一条消息之后,然后我们重启RabbitMQ服务器,查看web管理页面会发现,消息还在;

5.1 发送消息

/**
 * 消息接受者
 * Created by Kak on 2020/9/21.
 */
public class ConsumerMessage {
    static String queueName = "hellox";

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //配置信道绑定的队列为持久化队列
        channel.queueDeclare(queueName, true, false, false, null);
        System.out.println("准备接收消息!!!");
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("consumer--------" + new String(body, "UTF-8"));
            }
        };
        /**
         * 1:侦听的队列
         * 2:是否自动提醒
         * 3:接收消息后回调执行的逻辑
         */
        channel.basicConsume(queueName, true, consumer);
        //使用标准输入中断
        System.in.read();
        channel.close();
        connection.close();
    }
}

5.2 接收消息

/**
 * 生产者,消息发送者
 * 持久化队列
 * Created by Kak on 2020/9/21.
 */

public class ProducerMessage {
    static String queueName = "hellox";

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);//编程端口
        connectionFactory.setUsername("kaka");//本机可以使用guest
        connectionFactory.setPassword("kaka");
        connectionFactory.setVirtualHost("/");
        //创建连接
        Connection connection = connectionFactory.newConnection();
        //创建信道
        Channel channel = connection.createChannel();
        //配置信道绑定的队列为持久化队列
        channel.queueDeclare(queueName, true, false, false, null);
        //创建消息
        String msg = "This is rabbitMQ message!!!";
        //发送消息
        channel.basicPublish("", queueName, null, msg.getBytes());
        //释放资源
        channel.close();
        connection.close();
    }
}

在这里插入图片描述

你可能感兴趣的:(RabbitMQ,rabbitmq,java,队列)