RabbitMQ(六)【入门案例】

六、RabbitMQ 入门案例


上一篇文章AMQP协议

实现方式是:代码 + web图形界面

6.1 Simple 模式

在这里插入图片描述

实现步骤

  1. jdk1.8
  2. 构建一个maven工程
  3. 导入 rabbitmq 的 maven 依赖
  4. 启动 rabbitmq-server 服务
  5. 定义生产者
  6. 定义消费者
  7. 观察消息在 rabbitmq-server 服务中的过程

代码实现

1)创建一个maven工程

在这里插入图片描述

2)导入依赖

	<!--RabbitMQ-->
    <dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.14.2</version>
        </dependency>
    </dependencies>

3)启动 rabbitmq-server 服务

在这里插入图片描述

4)定义生产者

package com.vinjcent.rabbitmq.simple;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {

    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 2.创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 3.通过连接获取通道Channel
            channel = connection.createChannel();
            // 4.通过创建交换机、声明队列、绑定关系,路由key,发送消息和接收消息
            String queueName = "queue1";
            /*
             * @param1 队列的名字
             * @param2 是否需要持久化durable,所谓的持久化消息就是是否存盘
             * @param3 排他性,是否独占队列
             * @param4 是否自动删除,随着最后一个消费者消息完毕,消息以后是否把队列自动删除
             * @param5 携带一些附加参数
             *
             */
            channel.queueDeclare(queueName, false,false,false,null);
            // 5.准备消息内容
            String message = "hello world";
            // 6.发送消息给队列queue
            channel.basicPublish("", queueName,null,message.getBytes());

        } catch (Exception e) {
            // 7.关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                } catch (TimeoutException timeoutException) {
                    timeoutException.printStackTrace();
                }
            }
            // 8.关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }


    }

}

5)定义消费者

package com.vinjcent.rabbitmq.simple;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Consumer {

    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 2.创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 3.通过连接获取通道Channel
            channel = connection.createChannel();


            // 4.接收queue1队列的消息
            channel.basicConsume("queue1", true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println("收到的消息是" + new String(delivery.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                    System.out.println("接收消息失败!");
                }
            });

            System.out.println("开始接收消息");
            System.in.read();

        } catch (Exception e) {
            // 5.关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                } catch (TimeoutException timeoutException) {
                    timeoutException.printStackTrace();
                }
            }
            // 6.关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }


    }

}

运行生产者

如果没有达到下图的效果,请继续往下看

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

消息生产失败问题解决

用户权限问题

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

配置好后重新运行Producer即可

运行消费者

在这里插入图片描述

在这里插入图片描述

接受完消息之后,会发现有两个正在连接着broker
在这里插入图片描述

通过web界面实现

界面模拟生产者投递消息

  1. 创建一个queue2队列
    在这里插入图片描述
    在这里插入图片描述

  2. 选择默认交换机进行消息生产

在这里插入图片描述
在这里插入图片描述

  1. 查看queue2队列中的消息

在这里插入图片描述

  1. 接收队列queue2的消息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

小结

  • 持久化队列会存盘;非持久化队列也会存盘,但是会随着重启服务丢失

  • 如果没有声明交换机,那么该交换机是一个默认的交换机

  • 发送消息一定是交换机进行发送,而不是队列去发送,交换机接收消息后会推送到队列,所有的消费者会监听和订阅队列,交换机就会推送至消费者

6.2 Fanout 模式

在这里插入图片描述

方式一:通过界面实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

根据队列绑定交换机

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
根据交换机绑定队列

在这里插入图片描述

在这里插入图片描述

通过上面的例子说明,队列与交换机的绑定在可视化界面中有两种绑定

  • 通过队列绑定已存在的交换机
  • 通过交换机绑定已存在的队列

进入queue3队列查看绑定信息

在这里插入图片描述

进入交换机查看绑定的队列

在这里插入图片描述

在fanout-exchange交换机中发布消息
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

queue2队列中获取消息

在这里插入图片描述
在这里插入图片描述

方式二:通过代码实现

在这里插入图片描述

在这里插入图片描述

将队列与交换机进行绑定

在这里插入图片描述

在这里插入图片描述

运行生产者

package com.vinjcent.rabbitmq.routing;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {

    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2.设置连接属性
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 3.创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 4.通过连接获取通道Channel
            channel = connection.createChannel();
            // 5.准备消息内容
            String message = "hello world";
            // 6.准备交换机
            String exchangeName = "fanout-exchange";
            // 7.定义路由key
            String routeKey = "";
            // 8.指定交换机类型
            String type = "fanout";
            // 9.发送消息给中间件rabbitmq-server
            /*
             * @param1 交换机
             * @param2 队列、路由key
             * @param3 属性配置
             * @param4 消息内容
             * 可以存在没有交换机的队列码?不可能,虽然没有指定交换机,但是一定会存在一个默认的交换机
             */
            channel.basicPublish(exchangeName, routeKey,null,message.getBytes());
            System.out.println("消息发送成功!");
        } catch (Exception e) {
            // 10.关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            // 11.关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }


    }

}

在这里插入图片描述

运行消费者

package com.vinjcent.rabbitmq.routing;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Consumer {

    public static void main(String[] args) {
        // 使用多线程实时监听

        new Thread(runnable,"queue1").start();
        new Thread(runnable,"queue2").start();
        new Thread(runnable,"queue3").start();


    }

    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
            // ip port

            // 1.创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            // 2.设置连接属性
            connectionFactory.setHost("192.168.159.100");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            connectionFactory.setVirtualHost("/");

            // 获取队列的名称
            final String queueName = Thread.currentThread().getName();

            Connection connection = null;
            Channel channel = null;

            try {
                // 3.从连接工厂中获取连接
                connection = connectionFactory.newConnection("生产者");
                // 4.通过连接获取通道Channel
                channel = connection.createChannel();

                // 5.定义接收消息的回调
                channel.basicConsume(queueName, true, new DeliverCallback() {
                    @Override
                    public void handle(String s, Delivery delivery) throws IOException {
                        System.out.println(queueName + "收到的消息是. " + new String(delivery.getBody(), "UTF-8"));
                    }
                }, new CancelCallback() {
                    @Override
                    public void handle(String s) throws IOException {
                        System.out.println("接收消息失败!");
                    }
                });

                System.out.println("开始接收消息");
                System.in.read();

            } catch (Exception e) {
                // 6.关闭连接
                if (channel != null && channel.isOpen()){
                    try {
                        channel.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    } catch (TimeoutException timeoutException) {
                        timeoutException.printStackTrace();
                    }
                }
                // 7.关闭通道
                if (connection != null && connection.isOpen()){
                    try {
                        connection.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
            }
        }
    };
}

在这里插入图片描述

在这里插入图片描述

删除fanout交换机绑定关系,以及清空队列中的消息
在这里插入图片描述
在这里插入图片描述

6.3 Direct 模式

在这里插入图片描述

方式一:通过界面实现

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

方式二:通过代码实现

在这里插入图片描述

在这里插入图片描述

运行生产者

在这里插入图片描述

在这里插入图片描述

运行消费者

package com.vinjcent.rabbitmq.routing;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Consumer {

    public static void main(String[] args) {
        // 使用多线程实时监听

        new Thread(runnable,"queue1").start();
        new Thread(runnable,"queue2").start();
        new Thread(runnable,"queue3").start();


    }

    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
            // ip port

            // 1.创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            // 2.设置连接属性
            connectionFactory.setHost("192.168.159.100");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            connectionFactory.setVirtualHost("/");

            // 获取队列的名称
            final String queueName = Thread.currentThread().getName();

            Connection connection = null;
            Channel channel = null;

            try {
                // 3.从连接工厂中获取连接
                connection = connectionFactory.newConnection("生产者");
                // 4.通过连接获取通道Channel
                channel = connection.createChannel();

                // 5.定义接收消息的回调
                channel.basicConsume(queueName, true, new DeliverCallback() {
                    @Override
                    public void handle(String s, Delivery delivery) throws IOException {
                        System.out.println(queueName + "收到的消息是. " + new String(delivery.getBody(), "UTF-8"));
                    }
                }, new CancelCallback() {
                    @Override
                    public void handle(String s) throws IOException {
                        System.out.println("接收消息失败!");
                    }
                });

                System.out.println("开始接收消息");
                System.in.read();

            } catch (Exception e) {
                // 6.关闭连接
                if (channel != null && channel.isOpen()){
                    try {
                        channel.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    } catch (TimeoutException timeoutException) {
                        timeoutException.printStackTrace();
                    }
                }
                // 7.关闭通道
                if (connection != null && connection.isOpen()){
                    try {
                        connection.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
            }


        }
    };



}

在这里插入图片描述

小结使用 Direct 模式有一定的性能损耗,因为需要经过路由key过滤

fanout模式下指定路由key是没有效果的,默认交换机是 Direct 模式

6.4 Topic 模式理解

在这里插入图片描述

方式一:通过界面实现

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

方式二:通过代码实现

在这里插入图片描述

运行生产者

在这里插入图片描述

运行消费者


package com.vinjcent.rabbitmq.routing;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Consumer {

    public static void main(String[] args) {
        // 使用多线程实时监听

        new Thread(runnable,"queue1").start();
        new Thread(runnable,"queue2").start();
        new Thread(runnable,"queue3").start();
        new Thread(runnable,"queue4").start();
    }

    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
            // ip port

            // 1.创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            // 2.设置连接属性
            connectionFactory.setHost("192.168.159.100");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            connectionFactory.setVirtualHost("/");

            // 获取队列的名称
            final String queueName = Thread.currentThread().getName();

            Connection connection = null;
            Channel channel = null;

            try {
                // 3.从连接工厂中获取连接
                connection = connectionFactory.newConnection("生产者");
                // 4.通过连接获取通道Channel
                channel = connection.createChannel();

                // 5.定义接收消息的回调
                channel.basicConsume(queueName, true, new DeliverCallback() {
                    @Override
                    public void handle(String s, Delivery delivery) throws IOException {
                        System.out.println(queueName + "收到的消息是. " + new String(delivery.getBody(), "UTF-8"));
                    }
                }, s -> System.out.println("接收消息失败!"));

                System.out.println("开始接收消息");
                System.in.read();

            } catch (Exception e) {
                // 6.关闭连接
                if (channel != null && channel.isOpen()){
                    try {
                        channel.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    } catch (TimeoutException timeoutException) {
                        timeoutException.printStackTrace();
                    }
                }
                // 7.关闭通道
                if (connection != null && connection.isOpen()){
                    try {
                        connection.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
            }


        }
    };



}


在这里插入图片描述

小结

  • #代表匹配多个词,必须要填写
  • *代表匹配一个词,可以不需填写

6.5 Headers 模式

在这里插入图片描述

在这里插入图片描述

带有响应头参数发送消息

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

6.6 完整的声明创建方式

  1. 观察web界面的队列和交换机

查看队列

在这里插入图片描述

查看交换机

在这里插入图片描述

  1. 编写创建队列和交换机代码的生产者,并运行
package com.vinjcent.rabbitmq.all;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;

public class Producer {

    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2.设置连接属性
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 3.创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 4.通过连接获取通道Channel
            channel = connection.createChannel();
            // 5.准备消息内容
            String message = "hello world";
            // 6.准备交换机
            String exchangeName = "direct_message_exchange";
            // 7.指定交换机类型
            String exchangeType = "direct";
            // 声明交换机所谓的持久化就是指,交换机不会随着服务器重启造成丢失,如果true代表不丢失,false重启就会丢失
            channel.exchangeDeclare(exchangeName, exchangeType,true);
            // 8.声明队列
            channel.queueDeclare("queue5", true,false,false,null);
            channel.queueDeclare("queue6", true,false,false,null);
            channel.queueDeclare("queue7", true,false,false,null);

            // 9.绑定队列与交换机的关系
            channel.queueBind("queue5",exchangeName,"order");
            channel.queueBind("queue6",exchangeName,"stock");
            channel.queueBind("queue7",exchangeName,"order");

            // 10.发送消息给中间件rabbitmq-server
            /*
             * @param1 交换机
             * @param2 队列、路由key
             * @param3 属性配置
             * @param4 消息内容
             * 可以存在没有交换机的队列码?不可能,虽然没有指定交换机,但是一定会存在一个默认的交换机
             */
            channel.basicPublish(exchangeName, "order",null,message.getBytes());
            System.out.println("消息发送成功!");
        } catch (Exception e) {
            // 11.关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            // 12.关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}
  1. 查看web界面

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  1. 测试消费者接收

package com.vinjcent.rabbitmq.all;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Consumer {

    public static void main(String[] args) {
        // 使用多线程实时监听

        new Thread(runnable,"queue5").start();
        new Thread(runnable,"queue6").start();
        new Thread(runnable,"queue7").start();


    }

    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
            // ip port

            // 1.创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            // 2.设置连接属性
            connectionFactory.setHost("192.168.159.100");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            connectionFactory.setVirtualHost("/");

            // 获取队列的名称
            final String queueName = Thread.currentThread().getName();

            Connection connection = null;
            Channel channel = null;

            try {
                // 3.从连接工厂中获取连接
                connection = connectionFactory.newConnection("生产者");
                // 4.通过连接获取通道Channel
                channel = connection.createChannel();

                // 5.定义接收消息的回调
                channel.basicConsume(queueName, true, new DeliverCallback() {
                    @Override
                    public void handle(String s, Delivery delivery) throws IOException {
                        System.out.println(queueName + "收到的消息是. " + new String(delivery.getBody(), "UTF-8"));
                    }
                }, new CancelCallback() {
                    @Override
                    public void handle(String s) throws IOException {
                        System.out.println("接收消息失败!");
                    }
                });

                System.out.println("开始接收消息");
                System.in.read();

            } catch (Exception e) {
                // 6.关闭连接
                if (channel != null && channel.isOpen()){
                    try {
                        channel.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    } catch (TimeoutException timeoutException) {
                        timeoutException.printStackTrace();
                    }
                }
                // 7.关闭通道
                if (connection != null && connection.isOpen()){
                    try {
                        connection.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                }
            }


        }
    };
}


在这里插入图片描述

小结

如果队列没有指定的交换机,会选择默认交换机,默认交换机是 direct 模式;当消费者接收队列里的消息时,如果队列不存在,则会抛出异常

6.7 Work 轮询模式

当有多个消费者时,我们的消息会被哪个消费者接收呢?我们又该如何均衡消费者消费信息的多少呢?

主要有两种模式

  • 轮询模式:一个消费者一条消息,按均分配
  • 公平分发:根据消费者的消费能力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配

Work 模式轮询模式(Round-Robin)

在这里插入图片描述

特点:该模式接收消息是当有多个消费者接入时,消费分配模式是一个消费者分配一条消息,直至消息消费完成

在这里插入图片描述

生产者

package com.vinjcent.rabbitmq.work.polling;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {

    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1:创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 2:创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 3:通过连接获取通道Channel
            channel = connection.createChannel();
            // 4:通过创建交换机、声明队列、绑定关系,路由key,发送消息和接收消息
            String queueName = "queue1";

            for (int i = 0; i < 20; i++) {
                // 5:准备消息内容
                String message = "hello world" + i;

                // 6:发送消息给队列queue
                /*
                 * @param1 交换机
                 * @param2 队列、路由key
                 * @param3 消息的状态控制
                 * @param4 消息内容
                 * 可以存在没有交换机的队列码?不可能,虽然没有指定交换机,但是一定会存在一个默认的交换机
                 */
                channel.basicPublish("", queueName,null,message.getBytes());
            }
        } catch (Exception e) {
            // 7:关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                } catch (TimeoutException timeoutException) {
                    timeoutException.printStackTrace();
                }
            }
            // 8:关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }


    }

}

两个消费者 Work1 和 Work2


// ===============================Work1===============================
package com.vinjcent.rabbitmq.work.polling;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Work1 {

    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 2.创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 3.通过连接获取通道Channel
            channel = connection.createChannel();
            // 4.通过创建交换机、声明队列、绑定关系,路由key,发送消息和接收消息

            // 6.接收queue1队列的消息
            channel.basicConsume("queue1", true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println("Work1-收到的消息是" + new String(delivery.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                    System.out.println("接收消息失败!");
                }
            });

            System.out.println("开始接收消息");
            System.in.read();

        } catch (Exception e) {
            // 7.关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                } catch (TimeoutException timeoutException) {
                    timeoutException.printStackTrace();
                }
            }
            // 8.关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}

// ===============================Work2===============================

package com.vinjcent.rabbitmq.work.polling;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Work2 {

    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 2.创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 3.通过连接获取通道Channel
            channel = connection.createChannel();
            // 4.通过创建交换机、声明队列、绑定关系,路由key,发送消息和接收消息

            // 6.接收queue1队列的消息
            channel.basicConsume("queue1", true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println("Work2-收到的消息是" + new String(delivery.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                    System.out.println("接收消息失败!");
                }
            });

            System.out.println("开始接收消息");
            System.in.read();

        } catch (Exception e) {
            // 7.关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                } catch (TimeoutException timeoutException) {
                    timeoutException.printStackTrace();
                }
            }
            // 8.关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}


  1. 先启动 Work1 和 Work2 实时监听消息队列
  2. 再启动 producer

在这里插入图片描述

在这里插入图片描述

可以看到在生产20条消息的时候,消费者实时监听模式是根据轮询分发的策略进行接收消息的

6.8 Work 公平分发

公平分发

这里的公平分发指的是,根据消费者消费能力为前提,去接收消息队列里的消息

生产者

package com.vinjcent.rabbitmq.work.weigh;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {

    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1:创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 2:创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 3:通过连接获取通道Channel
            channel = connection.createChannel();
            // 4:通过创建交换机、声明队列、绑定关系,路由key,发送消息和接收消息
            String queueName = "queue1";

            for (int i = 0; i < 20; i++) {
                // 5:准备消息内容
                String message = "hello world" + i;

                // 6:发送消息给队列queue
                /*
                 * @param1 交换机
                 * @param2 队列、路由key
                 * @param3 消息的状态控制
                 * @param4 消息内容
                 * 可以存在没有交换机的队列码?不可能,虽然没有指定交换机,但是一定会存在一个默认的交换机
                 */
                channel.basicPublish("", queueName,null,message.getBytes());
            }
        } catch (Exception e) {
            // 7:关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                } catch (TimeoutException timeoutException) {
                    timeoutException.printStackTrace();
                }
            }
            // 8:关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}

两个消费者 Work1 和 Work2(这里修改了两者的睡眠时间)

// ===============================Work1===============================
package com.vinjcent.rabbitmq.work.weigh;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Work1 {


    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 2.创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 3.通过连接获取通道Channel
            channel = connection.createChannel();
            // 4.定义接收消息的回调
            Channel backChannel = channel;

            // 6.接收queue1队列的消息
            // 指标定义,代表分发消息的单位,尽量不能太高(<20)
            backChannel.basicQos(1);
            // 公平分发必须手动改成false
            backChannel.basicConsume("queue1", false, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {

                    System.out.println("Work1-收到的消息是" + new String(delivery.getBody(), "UTF-8"));

                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 单条消费
                    backChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                    System.out.println("接收消息失败!");
                }
            });

            System.out.println("开始接收消息");
            System.in.read();

        } catch (Exception e) {
            // 7.关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                } catch (TimeoutException timeoutException) {
                    timeoutException.printStackTrace();
                }
            }
            // 8.关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }
    }
}

// ===============================Work2===============================

package com.vinjcent.rabbitmq.work.weigh;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Work2 {

    public static void main(String[] args) {

        // 所有的中间件技术都是基于tcp/ip协议基础之上构建新型的协议规范,只不过rabbitmq遵循的是amqp
        // ip port

        // 1.创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("192.168.159.100");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");

        Connection connection = null;
        Channel channel = null;

        try {
            // 2.创建连接Connections
            connection = connectionFactory.newConnection("生产者");
            // 3.通过连接获取通道Channel
            channel = connection.createChannel();
            // 4.定义接收消息的回调
            Channel backChannel = channel;

            // 6.接收queue1队列的消息
            // 指标定义,代表分发消息的单位,尽量不能太高(<20)
            backChannel.basicQos(1);
            // 公平分发必须手动改成false
            backChannel.basicConsume("queue1", false, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println("Work2-收到的消息是" + new String(delivery.getBody(), "UTF-8"));
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 单条消费
                    backChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                    System.out.println("接收消息失败!");
                }
            });

            System.out.println("开始接收消息");
            System.in.read();

        } catch (Exception e) {
            // 7.关闭连接
            if (channel != null && channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                } catch (TimeoutException timeoutException) {
                    timeoutException.printStackTrace();
                }
            }
            // 8.关闭通道
            if (connection != null && connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }
        }


    }

}

  1. 先启动 work1 和 work2 实时监听消息队列
  2. 再启动 producer

在这里插入图片描述

在这里插入图片描述

解释

由于设置为根据消费者的接收响应时间,将 work1 消费者设置 2s 接收一次消息,work2 消费者设置 1s 接收一次消息,根据不同消费者接收响应时间,生产者在生产消息之后,推送到队列 queue1 中,根据不同的处理消息时间,能者多劳

6.9 RabbitMQ 使用场景

解耦、削峰、异步

1)同步异步的问题(串行)

在这里插入图片描述

代码

public void createOrderFanout(){
    // 1.保存订单
    orderService.saveOrder();
    // 2.账户查询
    accountService.getAccount();
    // 3.发送email服务
    emailService.sendEmailMessage();
    // 4.发送app服务
    appService.sendAppMessage();
}

2)并行方式,异步线程池

并行方式:将订单信息写入数据库成功后的同时,发送注册短信,以上上个任务完成后,返回客户端。与串行的差别在于:并行的方式可以提高处理的时间

在这里插入图片描述

代码

public void createOrderFanout(){
    // 1.保存订单
    orderService.saveOrder();
    
    relationMessage();
}

public void relationMessage(){
    // 异步
    threadpool.submit(new Callable<Objcet> {
        public Object call(){
                // 2.账户查询
                accountService.getAccount();
        }
    });
    // 异步
    threadpool.submit(new Callable<Objcet> {
        public Object call(){
                // 3.发送email服务
                emailService.sendEmailMessage();
        }
    });
    // 异步
    threadpool.submit(new Callable<Objcet> {
        public Object call(){
                // 4.发送app服务
                appService.sendAppMessage();
        }
    });
}

存在问题

  • 耦合度高
  • 需要自己写线程池维护成本太高
  • 出现了消息可能会丢失,需要自己做消息补偿
  • 需要保证消息的可靠性
  • 需要保证服务器的高可用

异步消息队列方式

在这里插入图片描述

优点

  • 完全解耦,用MQ建立桥接
  • 有独立的线程池和运行模式
  • 出现了消息可能会丢失,MQ有持久化功能
  • 死信队列和消息转移等,能够解决可靠性
  • HA镜像模型提供高可用
  • 服务效率整体提高

高内聚,低耦合

在这里插入图片描述

下一篇文章SpringBoot案例

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Naijia_OvO

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

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

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

打赏作者

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

抵扣说明:

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

余额充值