RabbitMQ 学习笔记

课程笔记:消息队列-RabbitMQ篇章- 专栏 -KuangStudy

一、安装

1、安装MQ

1、普通安装

[root@localhost ~]# docker search rabbitMq # 搜索 MQ 版本
[root@localhost ~]# docker pull rabbitmq # 下载 MQ
[root@localhost ~]# docker run -d --hostname my-rabbit --name rabbit -p 15672:15672 -p 5673:5672 rabbitmq # 运行 MQ
[root@localhost ~]# docker ps -a # 查看 MQ 容器ID
[root@localhost ~]# docker exec -it 容器id /bin/bssh # 进入 MQ 内部
root@my-rabbit:/# rabbitmq-plugins enable rabbitmq_management # 安装 MQ 图形化界面
[root@localhost ~]# curl localhost:15672 # 测试是否安装成功

2、直接运行

# —hostname:指定容器主机名称
# —name:指定容器名称
# -p:将mq端口号映射到本地
# 或者运行时设置用户和密码
docker run -di --name myrabbit -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 15672:15672 -p 5672:5672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management
# 查看日志
docker logs -f myrabbit
​
> more xxx.log  查看日记信息
> netstat -naop | grep 5672 查看端口是否被占用
> ps -ef | grep 5672  查看进程
> systemctl stop 服务

2、添加用户

需要在 MQ 内部进行操作:直接运行可以忽略这步

root@my-rabbit:/# rabbitmqctl add_user admin 123456 # 添加 admin 用户
root@my-rabbit:/# rabbitmqctl set_user_tags admin administrator # 授予操作权限
root@my-rabbit:/# rabbitmqctl set_permissions -p / admin ".*" ".*" ".*" # 授予资源权限
root@my-rabbit:/# rabbitmqctl change_password admin admin # 修改密码
root@my-rabbit:/# rabbitmqctl delete_user admin 删除用户
root@my-rabbit:/# rabbitmqctl list_users # 查看用户列表

二、入门案例

一、Simple 简单模式<Hello RabbitMQ>

1、引入依赖

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.10.0</version>
</dependency>

2、创建结构包

3、生产者代码

package org.example.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 协议 而 mq 是遵循 amqp

        // 1:创建链接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // ip port
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            // 2:创建连接 Connection
            connection = connectionFactory.newConnection("生产者");
            // 3:通过链接获取通道 Channel
            channel = connection.createChannel();
            // 4:通过创建交换机 声明队列 绑定关系 路由 key 发送消息 和 接收消息
            String queueName = "queue1";
            /*
             * @params 01 队列的每次
             * @params 02 是否要持久化 durable = false 消息是否存盘 true 不持久化 false 持久化 | 非持久化会存盘吗?
             * @params 03 排他性 是否是独占队列
             * @params 04 是否自动删除 随着最后一个消费者消费完是否把队列删除
             * @params 05 携带附加参数
             */
            channel.queueDeclare(queueName, false, false, false, null);
            // 5:准备消息内容
            String message = "hello weilekaixin!!!";
            // 6:发送消息给队列 queue
            // @params 01 交换机 @params 02 队列、路由 key @params 03 消息是否持久化 @params 04 消息的内容
            // TODO : 面试题 : 可以存在没有交换机的队列吗? 不可能,虽然没有指定交换机但是一定会存在一个默认的交换机
            channel.basicPublish("", queueName, null, message.getBytes());
            System.out.println("消息发送成功");
        } catch (IOException | TimeoutException e) {
            throw new RuntimeException(e);
        } finally {
            // 7:关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException | TimeoutException e) {
                    throw new RuntimeException(e);
                }
            }
            // 8:关闭链接
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

4、消费者代码

package org.example.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 协议 而 mq 是遵循 amqp

        // 1:创建链接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // ip port
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            // 2:创建连接 Connection
            connection = connectionFactory.newConnection("生产者");
            // 3:通过链接获取通道 Channel
            channel = connection.createChannel();
            channel.basicConsume("queue1", true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery message) throws IOException {
                    System.out.println("收到消息是" + new String(message.getBody()));
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                    System.out.println("接受失败了...");
                }
            });
            System.out.println("开始接受消息");
            System.in.read();
        } catch (IOException | TimeoutException e) {
            throw new RuntimeException(e);
        } finally {
            // 7:关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException | TimeoutException e) {
                    throw new RuntimeException(e);
                }
            }
            // 8:关闭链接
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

5、测试

6、总结

二、fanout 发布订阅模式

1、生产者代码

package org.example.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 协议 而 mq 是遵循 amqp

        // 1:创建链接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // ip port
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            // 2:创建连接 Connection
            connection = connectionFactory.newConnection("生产者");
            // 3:通过链接获取通道 Channel
            channel = connection.createChannel();
            // 4:通过创建交换机 声明队列 绑定关系 路由 key 发送消息 和 接收消息
            String queueName = "queue1";
            /*
             * @params 01 队列的每次
             * @params 02 是否要持久化 durable = false 消息是否存盘 true 不持久化 false 持久化 | 非持久化会存盘吗?
             * @params 03 排他性 是否是独占队列
             * @params 04 是否自动删除 随着最后一个消费者消费完是否把队列删除
             * @params 05 携带附加参数
             */
            channel.queueDeclare(queueName, false, false, false, null);
            // 5:准备消息内容
            String message = "hello weilekaixin!!!";
            // 6:准备交换机
            String exchangeName = "fanout-exchange";
            // 7:定义路由key
            String routeKey = "";
            // 8:指定交换机类型
            String type = "fanout";
            // 9:发送消息给队列 queue
            // @params 01 交换机 @params 02 队列、路由 key @params 03 消息是否持久化 @params 04 消息的内容
            // TODO : 面试题 : 可以存在没有交换机的队列吗? 不可能,虽然没有指定交换机但是一定会存在一个默认的交换机
            channel.basicPublish(exchangeName, routeKey, null, message.getBytes());
            System.out.println("消息发送成功");
        } catch (IOException | TimeoutException e) {
            throw new RuntimeException(e);
        } finally {
            // 10:关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException | TimeoutException e) {
                    throw new RuntimeException(e);
                }
            }
            // 11:关闭链接
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

2、消费者代码

package org.example.rabbitmq.routing;

import com.rabbitmq.client.*;

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

public class Consumer {
    private static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 所有中间件 都是遵循 tcp 协议 而 mq 是遵循 amqp

            // 1:创建链接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            // ip port
            connectionFactory.setHost("192.168.1.7");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            connectionFactory.setVirtualHost("/");
            final String queueName = Thread.currentThread().getName();
            Connection connection = null;
            Channel channel = null;
            try {
                // 2:创建连接 Connection
                connection = connectionFactory.newConnection("生产者");
                // 3:通过链接获取通道 Channel
                channel = connection.createChannel();
                channel.basicConsume(queueName, true, new DeliverCallback() {
                    @Override
                    public void handle(String s, Delivery message) throws IOException {
                        System.out.println("收到消息是" + new String(message.getBody()));
                    }
                }, new CancelCallback() {
                    @Override
                    public void handle(String s) throws IOException {
                        System.out.println("接受失败了...");
                    }
                });
                System.out.println(queueName + "开始接受消息");
                System.in.read();
            } catch (IOException | TimeoutException e) {
                throw new RuntimeException(e);
            } finally {
                // 7:关闭通道
                if (channel != null && channel.isOpen()) {
                    try {
                        channel.close();
                    } catch (IOException | TimeoutException e) {
                        throw new RuntimeException(e);
                    }
                }
                // 8:关闭链接
                if (connection != null && connection.isOpen()) {
                    try {
                        connection.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    };

    public static void main(String[] args) {
        new Thread(runnable, "queue1").start();
        new Thread(runnable, "queue2").start();
        new Thread(runnable, "queue3").start();
    }
}

3、绑定关系

 4、测试

5、生产者发送消息

6、消费者接受消息

三、direct 路由模式

1、生产者代码

package org.example.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 协议 而 mq 是遵循 amqp

        // 1:创建链接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // ip port
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            // 2:创建连接 Connection
            connection = connectionFactory.newConnection("生产者");
            // 3:通过链接获取通道 Channel
            channel = connection.createChannel();
            // 4:通过创建交换机 声明队列 绑定关系 路由 key 发送消息 和 接收消息
            String queueName = "queue1";
            /*
             * @params 01 队列的每次
             * @params 02 是否要持久化 durable = false 消息是否存盘 true 不持久化 false 持久化 | 非持久化会存盘吗?
             * @params 03 排他性 是否是独占队列
             * @params 04 是否自动删除 随着最后一个消费者消费完是否把队列删除
             * @params 05 携带附加参数
             */
            channel.queueDeclare(queueName, false, false, false, null);
            // 5:准备消息内容
            String message = "hello weilekaixin!!!";
            // 6:准备交换机
            String exchangeName = "direct-exchange";
            // 7:定义路由key
            String routeKey = "email";
            // 8:指定交换机类型
            String type = "direct";
            // 9:发送消息给队列 queue
            // @params 01 交换机 @params 02 队列、路由 key @params 03 消息是否持久化 @params 04 消息的内容
            // TODO : 面试题 : 可以存在没有交换机的队列吗? 不可能,虽然没有指定交换机但是一定会存在一个默认的交换机
            channel.basicPublish(exchangeName, routeKey, null, message.getBytes());
            System.out.println("消息发送成功");
        } catch (IOException | TimeoutException e) {
            throw new RuntimeException(e);
        } finally {
            // 10:关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException | TimeoutException e) {
                    throw new RuntimeException(e);
                }
            }
            // 11:关闭链接
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

2、绑定关系 

3、生产者发送消息

4、消费者匹配路由key接收消息

四、topic 主题模式

1、生产者代码

package org.example.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 协议 而 mq 是遵循 amqp

        // 1:创建链接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // ip port
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        connectionFactory.setVirtualHost("/");
        Connection connection = null;
        Channel channel = null;
        try {
            // 2:创建连接 Connection
            connection = connectionFactory.newConnection("生产者");
            // 3:通过链接获取通道 Channel
            channel = connection.createChannel();
            // 4:通过创建交换机 声明队列 绑定关系 路由 key 发送消息 和 接收消息
            String queueName = "queue1";
            /*
             * @params 01 队列的每次
             * @params 02 是否要持久化 durable = false 消息是否存盘 true 不持久化 false 持久化 | 非持久化会存盘吗?
             * @params 03 排他性 是否是独占队列
             * @params 04 是否自动删除 随着最后一个消费者消费完是否把队列删除
             * @params 05 携带附加参数
             */
            channel.queueDeclare(queueName, false, false, false, null);
            // 5:准备消息内容
            String message = "hello weilekaixin!!!";
            // 6:准备交换机
            String exchangeName = "topic-exchange";
            // 7:定义路由key
            String routeKey = "com.order.test.xxx";
            // 8:指定交换机类型
            String type = "topic";
            // 9:发送消息给队列 queue
            // @params 01 交换机 @params 02 队列、路由 key @params 03 消息是否持久化 @params 04 消息的内容
            // TODO : 面试题 : 可以存在没有交换机的队列吗? 不可能,虽然没有指定交换机但是一定会存在一个默认的交换机
            channel.basicPublish(exchangeName, routeKey, null, message.getBytes());
            System.out.println("消息发送成功");
        } catch (IOException | TimeoutException e) {
            throw new RuntimeException(e);
        } finally {
            // 10:关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException | TimeoutException e) {
                    throw new RuntimeException(e);
                }
            }
            // 11:关闭链接
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

2、绑定关系

 3、生产者发送消息

 4、消费者接受消息

 

五、Work 工作模式 

1、轮询分发

1、生产者代码

package org.example.rabbitmq.work.lunxun;

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

public class Producer {
    public static void main(String[] args) {
        // 1: 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2: 设置连接属性
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            // 3: 从连接工厂中获取连接
            connection = connectionFactory.newConnection("生产者");
            // 4: 从连接中获取通道channel
            channel = connection.createChannel();
            // 6: 准备发送消息的内容
            //===============================end topic模式==================================
            for (int i = 1; i <= 20; i++) {
                //消息的内容
                String msg = "weilekaixin:" + i;
                // 7: 发送消息给中间件rabbitmq-server
                // @params1: 交换机exchange
                // @params2: 队列名称/routingkey
                // @params3: 属性配置
                // @params4: 发送消息的内容
                channel.basicPublish("", "queue1", null, msg.getBytes());
            }
            System.out.println("消息发送成功!");
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("发送消息出现异常...");
        } finally {
            // 7: 释放连接关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
}

2、消费者1

package org.example.rabbitmq.work.lunxun;

import com.rabbitmq.client.*;

import java.io.IOException;

public class Work1 {
    public static void main(String[] args) {
        // 1: 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2: 设置连接属性
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            // 3: 从连接工厂中获取连接
            connection = connectionFactory.newConnection("消费者-Work1");
            // 4: 从连接中获取通道channel
            channel = connection.createChannel();
            // 5: 申明队列queue存储消息
            /*
             *  如果队列不存在,则会创建
             *  Rabbitmq不允许创建两个相同的队列名称,否则会报错。
             *
             *  @params1: queue 队列的名称
             *  @params2: durable 队列是否持久化
             *  @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
             *  @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
             *  @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
             * */
            // 这里如果queue已经被创建过一次了,可以不需要定义
//            channel.queueDeclare("queue1", false, false, false, null);
            // 同一时刻,服务器只会推送一条消息给消费者
            // 6: 定义接受消息的回调
            Channel finalChannel = channel;
            finalChannel.basicQos(1);
            finalChannel.basicConsume("queue1", true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    try{
                        System.out.println("Work1-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
                        Thread.sleep(2000);
                    }catch(Exception ex){
                        ex.printStackTrace();
                    }
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                }
            });
            System.out.println("Work1-开始接受消息");
            System.in.read();
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("发送消息出现异常...");
        } finally {
            // 7: 释放连接关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
}

3、消费者2

package org.example.rabbitmq.work.lunxun;

import com.rabbitmq.client.*;

import java.io.IOException;

public class Work2 {
    public static void main(String[] args) {
        // 1: 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2: 设置连接属性
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            // 3: 从连接工厂中获取连接
            connection = connectionFactory.newConnection("消费者-Work2");
            // 4: 从连接中获取通道channel
            channel = connection.createChannel();
            // 5: 申明队列queue存储消息
            /*
             *  如果队列不存在,则会创建
             *  Rabbitmq不允许创建两个相同的队列名称,否则会报错。
             *
             *  @params1: queue 队列的名称
             *  @params2: durable 队列是否持久化
             *  @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
             *  @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
             *  @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
             * */
            // 这里如果queue已经被创建过一次了,可以不需要定义
            //channel.queueDeclare("queue1", false, true, false, null);
            // 同一时刻,服务器只会推送一条消息给消费者
            //channel.basicQos(1);
            // 6: 定义接受消息的回调
            Channel finalChannel = channel;
            finalChannel.basicQos(1);
            finalChannel.basicConsume("queue1", true, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    try{
                        System.out.println("Work2-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
                        Thread.sleep(200);
                    }catch(Exception ex){
                        ex.printStackTrace();
                    }
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                }
            });
            System.out.println("Work2-开始接受消息");
            System.in.read();
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("发送消息出现异常...");
        } finally {
            // 7: 释放连接关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
}

4、测试

生产者发送消息

 消费者1

消费者2

 2、公平分发

必须手动应答

 1、消费者1

package org.example.rabbitmq.work.fair;

import com.rabbitmq.client.*;

import java.io.IOException;

public class Work1 {
    public static void main(String[] args) {
        // 1: 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2: 设置连接属性
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            // 3: 从连接工厂中获取连接
            connection = connectionFactory.newConnection("消费者-Work1");
            // 4: 从连接中获取通道channel
            channel = connection.createChannel();
            // 5: 申明队列queue存储消息
            /*
             *  如果队列不存在,则会创建
             *  Rabbitmq不允许创建两个相同的队列名称,否则会报错。
             *
             *  @params1: queue 队列的名称
             *  @params2: durable 队列是否持久化
             *  @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
             *  @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
             *  @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
             * */
            // 这里如果queue已经被创建过一次了,可以不需要定义
//            channel.queueDeclare("queue1", false, false, false, null);
            // 同一时刻,服务器只会推送一条消息给消费者
            // 6: 定义接受消息的回调
            Channel finalChannel = channel;
            finalChannel.basicQos(1);
            finalChannel.basicConsume("queue1", false, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    try {
                        System.out.println("Work1-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
                        Thread.sleep(2000);
                        finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                }
            });
            System.out.println("Work1-开始接受消息");
            System.in.read();
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("发送消息出现异常...");
        } finally {
            // 7: 释放连接关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
}

2、消费者2

package org.example.rabbitmq.work.fair;

import com.rabbitmq.client.*;

import java.io.IOException;

public class Work2 {
    public static void main(String[] args) {
        // 1: 创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 2: 设置连接属性
        connectionFactory.setHost("192.168.1.7");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            // 3: 从连接工厂中获取连接
            connection = connectionFactory.newConnection("消费者-Work2");
            // 4: 从连接中获取通道channel
            channel = connection.createChannel();
            // 5: 申明队列queue存储消息
            /*
             *  如果队列不存在,则会创建
             *  Rabbitmq不允许创建两个相同的队列名称,否则会报错。
             *
             *  @params1: queue 队列的名称
             *  @params2: durable 队列是否持久化
             *  @params3: exclusive 是否排他,即是否私有的,如果为true,会对当前队列加锁,其他的通道不能访问,并且连接自动关闭
             *  @params4: autoDelete 是否自动删除,当最后一个消费者断开连接之后是否自动删除消息。
             *  @params5: arguments 可以设置队列附加参数,设置队列的有效期,消息的最大长度,队列的消息生命周期等等。
             * */
            // 这里如果queue已经被创建过一次了,可以不需要定义
            //channel.queueDeclare("queue1", false, true, false, null);
            // 同一时刻,服务器只会推送一条消息给消费者
            //channel.basicQos(1);
            // 6: 定义接受消息的回调
            Channel finalChannel = channel;
            finalChannel.basicQos(1);
            finalChannel.basicConsume("queue1", false, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    try{
                        System.out.println("Work2-收到消息是:" + new String(delivery.getBody(), "UTF-8"));
                        Thread.sleep(200);
                        finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
                    }catch(Exception ex){
                        ex.printStackTrace();
                    }
                }
            }, new CancelCallback() {
                @Override
                public void handle(String s) throws IOException {
                }
            });
            System.out.println("Work2-开始接受消息");
            System.in.read();
        } catch (Exception ex) {
            ex.printStackTrace();
            System.out.println("发送消息出现异常...");
        } finally {
            // 7: 释放连接关闭通道
            if (channel != null && channel.isOpen()) {
                try {
                    channel.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            if (connection != null && connection.isOpen()) {
                try {
                    connection.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }
}

3、生产者发送消息

消费者1消费消息

4、消费者2消费消息 

三、应用场景

linsten to me feifei

解耦、削峰、异步

01-1、同步异步的问题(串行)

 串行代码

public void makeOrder(){
    // 1 :保存订单 
    orderService.saveOrder();
    // 2: 发送短信服务
    messageService.sendSMS("order");//1-2 s
    // 3: 发送email服务
    emailService.sendEmail("order");//1-2 s
    // 4: 发送APP服务
    appService.sendApp("order");    
}

01-2、并行方式 异步线程池 

 异步代码

public void makeOrder(){
    // 1 :保存订单 
    orderService.saveOrder();
   // 相关发送
   relationMessage();
}
public void relationMessage(){
    // 异步
     theadpool.submit(new Callable<Object>{
         public Object call(){
             // 2: 发送短信服务  
             messageService.sendSMS("order");
         }
     })
    // 异步
     theadpool.submit(new Callable<Object>{
         public Object call(){
              // 3: 发送email服务
            emailService.sendEmail("order");
         }
     })
      // 异步
     theadpool.submit(new Callable<Object>{
         public Object call(){
             // 4: 发送短信服务
             appService.sendApp("order");
         }
     })
      // 异步
         theadpool.submit(new Callable<Object>{
         public Object call(){
             // 4: 发送短信服务
             appService.sendApp("order");
         }
     })
}

01-2、异步消息队列的方式 

public void makeOrder(){
    // 1 :保存订单 
    orderService.saveOrder();   
    rabbitTemplate.convertSend("ex","2","消息内容");
}

四、SpringBoot 案例

1、初始化项目

1、创建SpringBoot项目

2、选择SpringWeb 和 Spring or RabbitMQ

2、配置yml 

# 服务端口
server:
  port: 8080
# 配置rabbitmq服务
spring:
  rabbitmq:
    username: admin
    password: admin
    virtual-host: /
    host: 192.168.1.7
    port: 5672

一、fanout 模式

1、配置config

package com.example.producer.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMqConfiguration {
    // 1.生命注册 fanout 模式交换机
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanout_order_exchange", true, false);
    }

    // 2.声明队列 sms.fanout.queue.email.fanout.queue.duanxin.fanout.queue
    @Bean
    public Queue smsQueue() {
        return new Queue("sms.fanout.queue", true);
    }

    @Bean
    public Queue duanxinQueue() {
        return new Queue("duanxin.fanout.queue", true);
    }

    @Bean
    public Queue emailQueue() {
        return new Queue("email.fanout.queue", true);
    }

    @Bean
    public Binding smsBinding() {
        return BindingBuilder.bind(smsQueue()).to(fanoutExchange());
    }

    @Bean
    public Binding emailBinding() {
        return BindingBuilder.bind(emailQueue()).to(fanoutExchange());
    }

    @Bean
    public Binding duanxinBinding() {
        return BindingBuilder.bind(duanxinQueue()).to(fanoutExchange());
    }
}

2、Order 接口

package com.example.producer.service;

import org.springframework.amqp.rabbit.core.RabbitTemplate;

import javax.annotation.Resource;
import java.util.UUID;

public class OrderService {

    @Resource
    private RabbitTemplate rabbitTemplate;

    public void makeOrder(String userid, String productid, int num) {
        // 1.根据商品ID查询库存是否充足
        // 2.保存订单
        String orderId = UUID.randomUUID().toString();
        System.out.println("订单生成成功" + orderId);
        // 3.通过MQ来完成消息分发
        // 参数1:交换机   参数2:路由key/queue队列名 参数3:消息内容
        String exchangeName = "fanout_order_exchange";
        String routingKey = "";
        rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId);
    }
}

3、生产者创建订单

4、创建消费者 

5、消费者代码

package com.example.consumer.service.fanout;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(queues = {"duanxin.fanout.queue"})
public class FanoutDuanxinConsumer {

    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("duanxin fanout --- 接收到了订单信息是:->" + message);
    }

}
package com.example.consumer.service.fanout;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(queues = {"email.fanout.queue"})
public class FanoutEmailConsumer {
    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("email fanout --- 接收到了订单信息是:->" + message);
    }

}
package com.example.consumer.service.fanout;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(queues = {"sms.fanout.queue"})
public class FanoutSMSConsumer {
    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("SMS fanout --- 接收到了订单信息是:->" + message);
    }

}

6、消费者消费信息 

二、Direct 模式

 1、配置类代码

package com.example.producer.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DirectRabbitMqConfiguration {
    // 1.生命注册 direct 模式交换机
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange("direct_order_exchange", true, false);
    }

    // 2.声明队列 sms.direct.queue.email.direct.queue.duanxin.direct.queue
    @Bean
    public Queue directSmsQueue() {
        return new Queue("sms.direct.queue", true);
    }

    @Bean
    public Queue directDuanxinQueue() {
        return new Queue("duanxin.direct.queue", true);
    }

    @Bean
    public Queue directEmailQueue() {
        return new Queue("email.direct.queue", true);
    }

    @Bean
    public Binding directSmsBinding() {
        return BindingBuilder.bind(directSmsQueue()).to(directExchange()).with("sms");
    }

    @Bean
    public Binding directEmailBinding() {
        return BindingBuilder.bind(directEmailQueue()).to(directExchange()).with("email");
    }

    @Bean
    public Binding directDuanxinBinding() {
        return BindingBuilder.bind(directDuanxinQueue()).to(directExchange()).with("duanxin");
    }
}

2、接口

public void makeOrderDirect(String userid, String productid, int num) {
        // 1.根据商品ID查询库存是否充足
        // 2.保存订单
        String orderId = UUID.randomUUID().toString();
        System.out.println("订单生成成功" + orderId);
        // 3.通过MQ来完成消息分发
        // 参数1:交换机   参数2:路由key/queue队列名 参数3:消息内容
        String exchangeName = "direct_order_exchange";
        rabbitTemplate.convertAndSend(exchangeName, "email", orderId);
        rabbitTemplate.convertAndSend(exchangeName, "duanxin", orderId);
    }

3、消费者代码

package com.example.consumer.service.direct;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(queues = {"duanxin.direct.queue"})
public class DirectDuanxinConsumer {

    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("duanxin direct --- 接收到了订单信息是:->" + message);
    }

}
package com.example.consumer.service.direct;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(queues = {"email.direct.queue"})
public class DirectEmailConsumer {
    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("email direct --- 接收到了订单信息是:->" + message);
    }

}
package com.example.consumer.service.direct;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(queues = {"sms.direct.queue"})
public class DirectSMSConsumer {
    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("SMS direct --- 接收到了订单信息是:->" + message);
    }

}

4、测试

 5、面试题

问:绑定关系配置在生产者好还是在消费者好

答:都可以  最好在消费者 因为消费要跟队列打交道

三、topic 模式

1、消费者代码

package com.example.consumer.service.topic;

import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "duanxin.topic.queue", durable = "true", autoDelete = "false"),
        exchange = @Exchange(value = "topic_order_exchange", type = ExchangeTypes.TOPIC),
        key = "#.duanxin.#"
))
public class TopicDuanxinConsumer {

    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("duanxin topic --- 接收到了订单信息是:->" + message);
    }

}
package com.example.consumer.service.topic;

import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "email.topic.queue", durable = "true", autoDelete = "false"),
        exchange = @Exchange(value = "topic_order_exchange", type = ExchangeTypes.TOPIC),
        key = "*.email.#"
))
public class TopicEmailConsumer {
    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("email topic --- 接收到了订单信息是:->" + message);
    }

}
package com.example.consumer.service.topic;

import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "sms.topic.queue", durable = "true", autoDelete = "false"),
        exchange = @Exchange(value = "topic_order_exchange", type = ExchangeTypes.TOPIC),
        key = "com.#"
))
public class TopicSMSConsumer {
    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("SMS topic --- 接收到了订单信息是:->" + message);
    }

}

2、接口

public void makeOrderTopic(String userid, String productid, int num) {
        // 1.根据商品ID查询库存是否充足
        // 2.保存订单
        String orderId = UUID.randomUUID().toString();
        System.out.println("订单生成成功" + orderId);
        // 3.通过MQ来完成消息分发
        // 参数1:交换机   参数2:路由key/queue队列名 参数3:消息内容
        String exchangeName = "topic_order_exchange";
        // #.duanxin.# duanxin
        // *.email.# email
        // com.# sms
        String routingKey = "com.email.duanxin";
        rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId);
    }

3、测试 

 

 五、RabbitMQ 高级

一、TTL 过期时间

1、消费者配置 

package com.example.consumer.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class TTLRabbitMqConfiguration {
    // 1.生命注册 direct 模式交换机
    @Bean
    public DirectExchange ttlExchange() {
        return new DirectExchange("ttl_direct_exchange", true, false);
    }

    // 2.声明队列 sms.direct.queue.email.direct.queue.duanxin.direct.queue
    @Bean
    public Queue ttlQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-message-ttl", 5000); // 这里一定是个 int 类型
        return new Queue("ttl.direct.queue", true, false, false, args);
    }


    @Bean
    public Binding ttlBindings() {
        return BindingBuilder.bind(ttlQueue()).to(ttlExchange()).with("sms");
    }
}

2、 生产者接口

public void makeOrderTTL(String userid, String productid, int num) {
        // 1.根据商品ID查询库存是否充足
        // 2.保存订单
        String orderId = UUID.randomUUID().toString();
        System.out.println("订单生成成功" + orderId);
        // 3.通过MQ来完成消息分发
        // 参数1:交换机   参数2:路由key/queue队列名 参数3:消息内容
        String exchangeName = "ttl_direct_exchange";
        String routingKey = "ttl";
        rabbitTemplate.convertAndSend(exchangeName, routingKey, orderId);
    }

3、测试

二、死信队列

  • 消息被拒绝
  • 消息过期
  • 队列达到最大长度

 1、TTL代码配置传参

 @Bean
    public Queue ttlQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-message-ttl", 5000); // 这里一定是个 int 类型
        args.put("x-dead-letter-exchange", "dead_direct_exchange");
        args.put("x-dead-letter-routing-key", "dead"); // fanout 不需要配
        return new Queue("ttl.direct.queue", true, false, false, args);
    }

2、死信队列配置

package com.example.consumer.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class DeadRabbitMqConfiguration {
    // 1.生命注册 direct 模式交换机
    @Bean
    public DirectExchange deadExchange() {
        return new DirectExchange("dead_direct_exchange", true, false);
    }

    // 2.声明队列 sms.direct.queue.email.direct.queue.duanxin.direct.queue
    @Bean
    public Queue deadQueue() {
        return new Queue("dead.direct.queue", true);
    }


    @Bean
    public Binding deadBindings() {
        return BindingBuilder.bind(deadQueue()).to(deadExchange()).with("sms");
    }
}

3、测试

 4、总结

可靠消费机制 类似回收站 

六、Rabbit 集群

七、Rabbit 分布式事务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值