RabbitMQ快速入门到高级【详解】

RabbitMQ

1、简介

1.1、什么是消息队列

MQ全称为Message Queue,即消息队列。“消息队列”是在消息的传输过程中保存消息的容器。它是典型的:生产者、消费者模型。生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,这样就实现了生产者和消费者的解耦。

1.2、消息队列使用场景
1、任务异步处理:

高并发环境下,由于来不及同步处理,请求往往会发生堵塞,比如说,大量的insert,update之类的请求同时到达MySQL,直接导致无数的行锁表锁,甚至最后请求会堆积过多,从而触发too many connections错误。通过使用消息队列,我们可以异步处理请求,从而缓解系统的压力。将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。减少了应用程序的响应时间。

2、应用程序解耦合:

MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。

3、RabbitMQ介绍

RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。

RabbitMQ官方地址:http://www.rabbitmq.com

4、RabbitMQ工作原理

img

组成部分说明:

Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue
Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过虑。
Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的
Producer:消息生产者,即生产方客户端,生产方客户端将消息发送
Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。

生产者发送消息流程:

1、生产者和Broker建立TCP连接。

2、生产者和Broker建立通道。

3、生产者通过通道消息发送给Broker,由Exchange将消息进行转发。

4、Exchange将消息转发到指定的Queue(队列)

消费者接收消息流程:

1、消费者和Broker建立TCP连接

2、消费者和Broker建立通道

3、消费者监听指定的Queue(队列)

4、当有消息到达Queue时Broker默认将消息推送给消费者。

5、消费者接收到消息。

6、ack回复

2、入门及安装

下载地址:https://lanzoui.com/b01hipv8b 密码:4eqm

下载完成上传至服务器

由于RabbitMQ是由erlang语言编写的,所以要先安装erlang环境

//先解压
rpm -Uvh erlang-22.0.7-1.el7.x86_64.rpm
//安装
yum install -y erlang
//查看是否安装成功
erl -v

在这里插入图片描述

表示erlang坏境安装成功

//安装socat
yum install -y socat
//解压
rpm -Uvh rabbitmq-server-3.7.18-1.el7.noarch.rpm
//安装
yum install rabbitmq-server -y
//启动
systemctl start rabbitmq-server
//查看状态
systemctl status rabbitmq-server.service

在这里插入图片描述

表示启动安装成功

//设置开机自启
systemctl enable rabbitmq-server

常用命令

//启动服务
systemctl start rabbitmq-server
//查看状态
systemctl status rabbitmq-server.servic
//停止服务
systemctl stop rabbitmq-server
//开机自启
systemctl enable rabbitmq-server

rabbitmq安装目录

 cd /usr/lib/rabbitmq/lib/rabbitmq_server-3.7.18/sbin

web管理界面及授权操作

浏览器访问

ip:15672

例如:http://192.168.1.100:15672/

如果访问不了,需要开启防火墙端口

iptables -I INPUT -p tcp --dport 15672 -j ACCEPT

顺便在开一个5672端口,后续为了java程序使用

iptables -I INPUT -p tcp --dport 5672 -j ACCEPT

再次访问 http://192.168.1.100:15672/

在这里插入图片描述

ok,成功访问,用户账号只能在本地访问,所以新建账号

3、授权账号和密码

添加用户

add_user admin admin

授予角色

rabbitmqctl set_user_tags admin administrator

查看用户组

rabbitmqctl list_users

使用admin登录成功访问

4、快速入门案列

1、导入依赖
  <!--原生依赖-->
        <!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.14.2</version>
        </dependency>
2、创建生产者
package edu.hunnan.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;

/**
 * Created by liangtong.
 */
public class Producer {
    public static void main(String[] args) {
        //1、创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setVirtualHost("/");
        connectionFactory.setPort(5672);
        connectionFactory.setHost("192.168.1.100");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            //2、创建连接Connection
             connection = connectionFactory.newConnection("生产者");
            //3、通过连接获取通道Channel
            channel = connection.createChannel();
            //4、通过交换机,声明队列,绑定关系,路由key.发送消息,和接受消息
            String name = "queue1";
            /**
             *参数详解
             * 1、队列的名字
             * 2、是否要持久化,持久化存盘
             * 3、排他性,是否是独占独立
             * 4、是否自动删除,随着最后一个消费者消息完毕是否把队列自动删除
             * 5、携带附属参数
             */
            channel.queueDeclare(name,false,false,false,null);
            //5、准备消息内容
            String massage = "Hello RabbiMQ!";
            //发送消息给队列queue
            channel.basicPublish("",name,null,massage.getBytes());
            System.err.println("消息发送成功!!");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            if(channel!=null&&channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null&&connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

运行可能会报错,解决方法

在这里插入图片描述

ok,发送成功!

3、创建消费者
package edu.hunnan.rabbitmq.simple;

import com.rabbitmq.client.*;

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

//消费者
public class Consumer {
    public static void main(String[] args) {
        //1、创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setVirtualHost("/");
        connectionFactory.setPort(5672);
        connectionFactory.setHost("192.168.1.100");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            //2、创建连接Connection
            connection = connectionFactory.newConnection("生产者");
            //3、通过连接获取通道Channel
            channel = connection.createChannel();
            //4、 定义消费方法
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String msg = new String(body, "utf-8");
                    System.out.println("收到的消息是:" + msg);
                }
            };
            channel.basicConsume("queue1", true, consumer);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            if(channel!=null&&channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null&&connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

ok,接受消息

5、RabbitMQ支持的消息模式

官方地址:https://www.rabbitmq.com/getstarted.html

1、简单模式

快速入门案例就是简单模式

2、Fanout模式

发布订阅

  1. 生产者

    package edu.hunnan.rabbitmq.fanout;
    
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    import java.io.IOException;
    import java.util.concurrent.TimeoutException;
    
    /**
     * Created by liangtong.
     *   fanout  发布订阅模式
     */
    public class Producer {
        public static void main(String[] args) {
            //1、创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setVirtualHost("/");
            connectionFactory.setPort(5672);
            connectionFactory.setHost("192.168.132.128");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            Connection connection = null;
            Channel channel = null;
            try {
                //2、创建连接Connection
                 connection = connectionFactory.newConnection("fanout模式");
                //3、通过连接获取通道Channel
                channel = connection.createChannel();
                //准备发送消息的内容
                String massage = "我是fanout模式的消息!我是发布订阅模式";
                //准备交换机(交换机必须存在,如果不存在则报错,这里的绑定关系已经在web界面绑定好了,所以这里没有绑定关系)
                String exchangeName ="fanout_exchange";
                //定义路由key(fanout模式不需要路由Key)
                String routekey="";
                //指定交互机类型
                String type ="fanout";
                //如果不指定交互机那么就是默认的交换机
                channel.basicPublish(exchangeName,routekey,null,massage.getBytes());
                System.err.println("消息发送成功!!");
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }finally {
                //关闭资源
                if(channel!=null&&channel.isOpen()){
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                if(connection!=null&&connection.isOpen()){
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }
    
  2. 消费者

    package edu.hunnan.rabbitmq.fanout;
    
    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() {
                //1、创建连接工程
                ConnectionFactory connectionFactory = new ConnectionFactory();
                connectionFactory.setVirtualHost("/");
                connectionFactory.setPort(5672);
                connectionFactory.setHost("192.168.132.128");
                connectionFactory.setUsername("admin");
                connectionFactory.setPassword("admin");
                //获取队列名称
                final String queueName = Thread.currentThread().getName();
                Connection connection = null;
                Channel channel = null;
                try {
                    //2、创建连接Connection
                    connection = connectionFactory.newConnection("fanout模式");
                    //3、通过连接获取通道Channel
                    channel = connection.createChannel();
                    //4、 定义消费方法
                    DefaultConsumer consumer = new DefaultConsumer(channel) {
                        @Override
                        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                            String msg = new String(body, "utf-8");
                            System.out.println(queueName+"收到的消息是" + msg);
                        }
                    };
                    channel.basicConsume(queueName, true, consumer);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }finally {
                    //关闭资源
                    if(channel!=null&&channel.isOpen()){
                        try {
                            channel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (TimeoutException e) {
                            e.printStackTrace();
                        }
                    }
                    if(connection!=null&&connection.isOpen()){
                        try {
                            connection.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
        public static void main(String[] args) {
            new Thread(runnable,"queue1").start();
            new Thread(runnable,"queue3").start();
        }
    }
    
    
3、Direct模式

路由key条件筛选

  1. 生产者

    package edu.hunnan.rabbitmq.direct;
    
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    import java.io.IOException;
    import java.util.concurrent.TimeoutException;
    
    /**
     * Created by liangtong.
     *   direct  发布订阅路由key模式
     */
    public class Producer {
        public static void main(String[] args) {
            //1、创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setVirtualHost("/");
            connectionFactory.setPort(5672);
            connectionFactory.setHost("192.168.132.128");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            Connection connection = null;
            Channel channel = null;
            try {
                //2、创建连接Connection
                 connection = connectionFactory.newConnection("direct模式");
                //3、通过连接获取通道Channel
                channel = connection.createChannel();
                //准备发送消息的内容
                String massage = "我是发布订阅路由key模式的消息!我是发布订阅路由key模式";
                //准备交换机(交换机必须存在,如果不存在则报错,这里的绑定关系已经在web界面绑定好了,所以这里没有绑定关系)
                String exchangeName ="direct_exchange";
                //定义路由key(fanout模式不需要路由Key)
                String routekey="a";//条件筛选,只给路由key是a的发消息
                //指定交互机类型
                String type ="direct";
                //如果不指定交互机那么就是默认的交换机
                channel.basicPublish(exchangeName,routekey,null,massage.getBytes());
                System.err.println("消息发送成功!!");
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }finally {
                //关闭资源
                if(channel!=null&&channel.isOpen()){
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                if(connection!=null&&connection.isOpen()){
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }
    
  2. 消费者

    package edu.hunnan.rabbitmq.direct;
    
    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() {
                //1、创建连接工程
                ConnectionFactory connectionFactory = new ConnectionFactory();
                connectionFactory.setVirtualHost("/");
                connectionFactory.setPort(5672);
                connectionFactory.setHost("192.168.132.128");
                connectionFactory.setUsername("admin");
                connectionFactory.setPassword("admin");
                //获取队列名称
                final String queueName = Thread.currentThread().getName();
                Connection connection = null;
                Channel channel = null;
                try {
                    //2、创建连接Connection
                    connection = connectionFactory.newConnection("direct模式");
                    //3、通过连接获取通道Channel
                    channel = connection.createChannel();
                    //4、 定义消费方法
                    DefaultConsumer consumer = new DefaultConsumer(channel) {
                        @Override
                        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                            String msg = new String(body, "utf-8");
                            System.out.println(queueName+"收到的消息是" + msg);
                        }
                    };
                    channel.basicConsume(queueName, true, consumer);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }finally {
                    //关闭资源
                    if(channel!=null&&channel.isOpen()){
                        try {
                            channel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (TimeoutException e) {
                            e.printStackTrace();
                        }
                    }
                    if(connection!=null&&connection.isOpen()){
                        try {
                            connection.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
    
    
        public static void main(String[] args) {
            new Thread(runnable,"queue1").start();
            new Thread(runnable,"queue3").start();
        }
    }
    
4、Topic(主题)模式

模糊匹配:#:表示一个或多个,一级或多级,*:表示由且只有一个和只有一级和必须要有一级

  1. 生产者

    package edu.hunnan.rabbitmq.topic;
    
    import com.rabbitmq.client.Channel;
    import com.rabbitmq.client.Connection;
    import com.rabbitmq.client.ConnectionFactory;
    
    import java.io.IOException;
    import java.util.concurrent.TimeoutException;
    
    /**
     * Created by liangtong.
     *   topic模式  主题模式
     */
    public class Producer {
        public static void main(String[] args) {
            //1、创建连接工程
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setVirtualHost("/");
            connectionFactory.setPort(5672);
            connectionFactory.setHost("192.168.132.128");
            connectionFactory.setUsername("admin");
            connectionFactory.setPassword("admin");
            Connection connection = null;
            Channel channel = null;
            try {
                //2、创建连接Connection
                 connection = connectionFactory.newConnection("topic模式");
                //3、通过连接获取通道Channel
                channel = connection.createChannel();
                //准备发送消息的内容
                String massage = "我是主题的消息!我是主题模式";
                //准备交换机(交换机必须存在,如果不存在则报错,这里的绑定关系已经在web界面绑定好了,所以这里没有绑定关系)
                String exchangeName ="topic_exchange";
                //定义路由key
                String routekey="com.a";//
                //指定交互机类型
                String type ="topic";
                //如果不指定交互机那么就是默认的交换机
                channel.basicPublish(exchangeName,routekey,null,massage.getBytes());
                System.err.println("消息发送成功!!");
            } catch (IOException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }finally {
                //关闭资源
                if(channel!=null&&channel.isOpen()){
                    try {
                        channel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    }
                }
                if(connection!=null&&connection.isOpen()){
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    }
    
  2. 消费者

    package edu.hunnan.rabbitmq.topic;
    
    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() {
                //1、创建连接工程
                ConnectionFactory connectionFactory = new ConnectionFactory();
                connectionFactory.setVirtualHost("/");
                connectionFactory.setPort(5672);
                connectionFactory.setHost("192.168.132.128");
                connectionFactory.setUsername("admin");
                connectionFactory.setPassword("admin");
                //获取队列名称
                final String queueName = Thread.currentThread().getName();
                Connection connection = null;
                Channel channel = null;
                try {
                    //2、创建连接Connection
                    connection = connectionFactory.newConnection("topic模式");
                    //3、通过连接获取通道Channel
                    channel = connection.createChannel();
                    //4、 定义消费方法
                    DefaultConsumer consumer = new DefaultConsumer(channel) {
                        @Override
                        public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                            String msg = new String(body, "utf-8");
                            System.out.println(queueName+"收到的消息是" + msg);
                        }
                    };
                    channel.basicConsume(queueName, true, consumer);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }finally {
                    //关闭资源
                    if(channel!=null&&channel.isOpen()){
                        try {
                            channel.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        } catch (TimeoutException e) {
                            e.printStackTrace();
                        }
                    }
                    if(connection!=null&&connection.isOpen()){
                        try {
                            connection.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        };
    
    
        public static void main(String[] args) {
            new Thread(runnable,"queue1").start();
            new Thread(runnable,"queue3").start();
        }
    
    
    }
    
    

6、完整的申明创建方式

package edu.hunnan.rabbitmq.all;

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

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

/**
 * Created by liangtong.
 *
 */
public class Producer {
    public static void main(String[] args) {
        //1、创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setVirtualHost("/");
        connectionFactory.setPort(5672);
        connectionFactory.setHost("192.168.132.128");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            //2、创建连接Connection
             connection = connectionFactory.newConnection("完整申明模式创建方式");
            //3、通过连接获取通道Channel
            channel = connection.createChannel();
            //准备发送消息的内容
            String massage = "我是完整申明模式创建方式";
            //准备交换机(交换机必须存在,如果不存在则报错,这里的绑定关系已经在web界面绑定好了,所以这里没有绑定关系)
            String exchangeName ="direct_exchange1";
            //指定交互机类型
            String type ="direct";
            //声明交换机
            channel.exchangeDeclare(exchangeName,type,true);
            //可以使用web界面进行声明和绑定队列,这里我们使用代码的方式进行声明绑定
            //声明队列(创建队列)
            channel.queueDeclare("queue5",true,false,false,null);
            channel.queueDeclare("queue6",true,false,false,null);
            //绑定队列和交换机的关系
            channel.queueBind("queue5",exchangeName,"order");
            channel.queueBind("queue6",exchangeName,"bbbbb");
            //路由key发送消息
            channel.basicPublish(exchangeName,"order",null,massage.getBytes());
            System.err.println("消息发送成功!!");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            if(channel!=null&&channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null&&connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

7、Work轮询模式

生产者
package edu.hunnan.rabbitmq.work.lunxun;

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

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

/**
 * Created by liangtong.
 *
 */
public class Producer {
    public static void main(String[] args) {
        //1、创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setVirtualHost("/");
        connectionFactory.setPort(5672);
        connectionFactory.setHost("192.168.132.128");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            //2、创建连接Connection
             connection = connectionFactory.newConnection("完整申明模式创建方式");
            //3、通过连接获取通道Channel
            channel = connection.createChannel();
            for (int i = 0; i <20 ; i++) {
                //消息的内容
                String msg ="我是轮询模式"+i;
                //默认如果没有指定交换机,那么路由Key指的是队列名称
                channel.basicPublish("","queue1",null,msg.getBytes());
            }
            System.err.println("消息发送成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            if(channel!=null&&channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null&&connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
消费者

两个消费者,其代码就是一致的

package edu.hunnan.rabbitmq.work.lunxun;

import com.rabbitmq.client.*;

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

//消费者
public class Work1 {
    public static void main(String[] args) {
        //1、创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setVirtualHost("/");
        connectionFactory.setPort(5672);
        connectionFactory.setHost("192.168.132.128");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            //2、创建连接Connection
            connection = connectionFactory.newConnection("轮询模式");
            //3、通过连接获取通道Channel
            channel = connection.createChannel();
            //4、 定义消费方法
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String msg = new String(body, "utf-8");
                    System.out.println("Work1收到的消息是:" + msg);
                }
            };
            channel.basicConsume("queue1", true, consumer);
            System.err.println("work1开始接收消息");
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            if(channel!=null&&channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null&&connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

先启动两个消费者,再启动生产者

8、Work公平模式

生产者
package edu.hunnan.rabbitmq.work.gongping;

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

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

/**
 * Created by liangtong.
 *
 */
public class Producer {
    public static void main(String[] args) {
        //1、创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setVirtualHost("/");
        connectionFactory.setPort(5672);
        connectionFactory.setHost("192.168.132.128");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        try {
            //2、创建连接Connection
             connection = connectionFactory.newConnection("公平");
            //3、通过连接获取通道Channel
            channel = connection.createChannel();
            for (int i = 0; i <20 ; i++) {
                //消息的内容
                String msg ="我是公平模式"+i;
                //默认如果没有指定交换机,那么路由Key指的是队列名称
                channel.basicPublish("","queue1",null,msg.getBytes());
            }
            System.err.println("消息发送成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            if(channel!=null&&channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null&&connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
消费者
package edu.hunnan.rabbitmq.work.gongping;

import com.rabbitmq.client.*;

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

//消费者
public class Work1 {
    public static void main(String[] args) {
        //1、创建连接工程
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setVirtualHost("/");
        connectionFactory.setPort(5672);
        connectionFactory.setHost("192.168.132.128");
        connectionFactory.setUsername("admin");
        connectionFactory.setPassword("admin");
        Connection connection = null;
        Channel channel = null;
        //定义接收消息的回调
        Channel finalchannel = channel;
        //指标定义出来 qos=1,同一时刻服务器只会推送一条消息给服务者

        try {
            //2、创建连接Connection
            connection = connectionFactory.newConnection("公平模式");
            //3、通过连接获取通道Channel
            channel = connection.createChannel();
            //4、 定义消费方法
            DefaultConsumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String msg = new String(body, "utf-8");
                    System.out.println("Work1收到的消息是:" + msg);
                    //手动应答
                    finalchannel.basicAck(envelope.getDeliveryTag(),false);
                }
            };
            channel.basicConsume("queue1", false, consumer);
            try {
                channel.basicQos(1);
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.err.println("work1开始接收消息");
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }finally {
            //关闭资源
            if(channel!=null&&channel.isOpen()){
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if(connection!=null&&connection.isOpen()){
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

区别:
  • 轮询模式的分发:一个消费者一条,按均分配;
  • 公平分发:根据消费者的消费能力进行公平分发,处理快的处理的多,处理慢的处理的少;按劳分配;
  • 时间不会影响公平分发策略

9、整合SpringBoot

1、Fanout模式
生产者
1、导入依赖
    <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
2、修改配置文件
server:
  port: 8080

#配置Rabbitmq服务
spring:
  rabbitmq:
    username: admin
    password: admin
    host: 192.168.132.128
    port: 5672
    virtual-host: /
3、添加配置文件
package edu.hunnan.rabbitmq.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 RabbitMQConfig {
    //声明注册fanout模式交换机
    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("fanout_exchange",true,false);
    }
    //声明队列(创建队列)
    @Bean
    public Queue smsQueue(){
         /**
         * 参数详解
         * durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
         * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
         * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
         * 一般设置一下队列的持久化就好,其余两个就是默认false
         */
        return new Queue("sms.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 emaliBinding(){
        return BindingBuilder.bind(emailQueue()).to(fanoutExchange());
    }
}
4、service
package edu.hunnan.rabbitmq.service;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class RabbitMQService {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     *
     * @param userid  用户id
     * @param productid  商品id
     * @param num 商品购买数量
     */
    public void make(String userid,String productid,int num){
        String orderid = UUID.randomUUID().toString();
        System.err.println("订单生成成功"+orderid);
        //通过MQ来完成消息的分发
        //交换机(如果该交换机不存在,则报错  整合SpringBoot如果没有交换机则会自动创建交换机)
        String exchangeName = "fanout_exchange"; 
        String routingkey =""; //必须指定routingkey routingkey可以为空
        //该方法表示消息分发
        rabbitTemplate.convertAndSend(exchangeName,routingkey,orderid);
    }
}

5、发送消息
@SpringBootTest
class SpringbootRabbitmqProducerApplicationTests {
    @Autowired
    private RabbitMQService rabbitMQService;
    @Test
    void contextLoads() {
        //发送消息
        rabbitMQService.make("1","1",12);
    }
}
消费者
1,2步骤一致
3、service
@Service
@RabbitListener(queues = "email.fanout.queue")
public class FanoutEmailService {
    @RabbitHandler
    public void reviceMessage(String message){
        System.err.println("email.fanout.queue->接受订单信息是:->"+message);
    }
}

2、Direct模式
生产者
1、service
package edu.hunnan.rabbitmq.service.direct;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.UUID;

@Service
public class DirectService {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     *
     * @param userid  用户id
     * @param productid  商品id
     * @param num 商品购买数量
     */
    public void make(String userid,String productid,int num){
        String orderid = UUID.randomUUID().toString();
        System.err.println("订单生成成功"+orderid);
        //通过MQ来完成消息的分发
        //交换机(如果该交换机不存在,则报错)
        String exchangeName = "direct_exchange";
        //该方法表示消息分发 路由Key满足条件收消息
        rabbitTemplate.convertAndSend(exchangeName,"sms",orderid);
    }
}

2、配置文件
package edu.hunnan.rabbitmq.config;

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

@Configuration
public class DirectConfig {
    //声明注册Direct模式交换机
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("direct_exchange",true,false);
    }
    //声明队列(创建队列)
    @Bean
    public Queue DirectsmsQueue(){
        return new Queue("sms.direct.queue",true);
    }
    @Bean
    public Queue DirectemailQueue(){
        return new Queue("email.direct.queue",true);
    }
    //完成绑定关系(队列和交换机完成绑定关系) with:设置路由key	
    @Bean
    public Binding DirectsmsBinding(){
        return BindingBuilder.bind(DirectsmsQueue()).to(directExchange()).with("sms");
    }
    @Bean
    public Binding DirectemaliBinding(){
        return BindingBuilder.bind(DirectemailQueue()).to(directExchange()).with("email");
    }
}

消费者

代码一致

满足条件收到消息

在这里插入图片描述

3、Topic模式
生产者
1、service
 public void make(String userid,String productid,int num){
        String orderid = UUID.randomUUID().toString();
        System.err.println("订单生成成功"+orderid);
        //通过MQ来完成消息的分发
        //交换机(如果该交换机不存在,则报错)
        String exchangeName = "topic_exchange";
        //该方法表示消息分发 路由Key满足条件收消息
        rabbitTemplate.convertAndSend(exchangeName,"#.sms.email",orderid);
    }
2、配置文件
package edu.hunnan.rabbitmq.config;

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

@Configuration
public class TopicConfig {
    //声明注册fanout模式交换机
    @Bean
    public TopicExchange topicExchange(){
        return new TopicExchange("topic_exchange",true,false);
    }
    //声明队列(创建队列)
    @Bean
    public Queue topicsmsQueue(){
        return new Queue("sms.topic.queue",true);
    }
    @Bean
    public Queue topicsemailQueue(){
        return new Queue("email.topic.queue",true);
    }
    //完成绑定关系(队列和交换机完成绑定关系)
    @Bean
    public Binding topicssmsBinding(){
        return BindingBuilder.bind(topicsmsQueue()).to(topicExchange()).with("#.sms.#");
    }
    @Bean
    public Binding topicsemaliBinding(){
        return BindingBuilder.bind(topicsemailQueue()).to(topicExchange()).with("*.email.*");
    }
}
消费者

代码一致

10、高级ttl队列过期时间

生产者
1、service
   public void make(String userid,String productid,int num){
        String orderid = UUID.randomUUID().toString();
        System.err.println("订单生成成功"+orderid);
        //通过MQ来完成消息的分发
        //交换机(如果该交换机不存在,则报错)
        String exchangeName = "ttl_direct_exchange";
        String routingkey = "ttl";
        //该方法表示消息分发 路由Key满足条件收消息
        rabbitTemplate.convertAndSend(exchangeName,routingkey,orderid);
    }
2、配置类
package edu.hunnan.rabbitmq.config;

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

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

@Configuration
public class TtlConfig {
    //声明注册direct模式交换机
    @Bean
    public DirectExchange ttlDirectExchange(){
        return new DirectExchange("ttl_direct_exchange",true,false);
    }
    //声明队列(创建队列)
    @Bean
    public Queue ttlDirectsmsQueue(){
        //队列的过期时间
        Map<String, Object> map = new HashMap<>();
        map.put("x-message-ttl",5000);//5秒后消息过期
        return new Queue("ttl.direct.queue",true,false,false,map);
    }
    @Bean
    public Binding ttlDirectsmsBinding(){
        return BindingBuilder.bind(ttlDirectsmsQueue()).to(ttlDirectExchange()).with("ttl");
    }
}

ttl队列过期时间:表示在规定的时间内如果消息没有被消息者消费则自动消费

如果用于订单场景中,30分钟后没有支付,该消息并不应该自动消费,而是存到死信队列中,由死信队列统一进行消费

11、高级死信队列

1、概念

死信,顾名思义就是无法被消费的消息,字面意思可以这样理解,一般来说,producer将消息投递到broker或者直接到queue里了,consumer从queue取出消息进行消费,但某些时候由于特定的原因导致queue中的某些消息无法被消费,这样的消息如果没有后续的处理,就变成了死信,有死信,自然就有了死信队列;

以上是个人的通俗解释,专业术语解释的比较正规点大家可以参考,主要想搞清楚这个概念,不同的消息中间件大概都有自身对于死信或者死信队列的处理方式,下面重点要说说。

消息变成死信有以下几种情况

消息被拒绝(basic.reject / basic.nack),并且requeue = false
消息TTL过期
队列达到最大长度

2、核心代码

创建死信队列

package edu.hunnan.rabbitmq.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;

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

/**
 * 死信队列
 */
@Configuration
public class DeadRabbiMQConfig {
    //交换机的名字
    public final String Exchange_Name="Dead_Exchange";
    //队列名字
    public final String Query_Name="Dead_Query";

    //交换机
    @Bean
    public FanoutExchange DeadExchange(){
        /**
         *  parameter:交换机的名字
         *  parameter:是否持久化
         *  parameter:是否自动删除
         */
        return new FanoutExchange(Exchange_Name,false,false);
    }
    //队列
    @Bean
    public Queue DeadQueue(){
        /**
         * parameter:队列名称
         * parameter:是否持久化
         */
        return new Queue(Query_Name,true);
    }
    //绑定
    @Bean
    public Binding DeadBind(){
        return BindingBuilder.bind(DeadQueue()).to(DeadExchange());
    }
}

在ttl队列中的配置类中进行拓展

package edu.hunnan.rabbitmq.config;

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

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

@Configuration
public class TtlConfig {
    //声明注册fanout模式交换机
    @Bean
    public DirectExchange ttlDirectExchange(){
        return new DirectExchange("ttl_direct_exchange",true,false);
    }
    //声明队列(创建队列)
    @Bean
    public Queue ttlDirectsmsQueue(){
        //队列的过期时间
        Map<String, Object> map = new HashMap<>();
        map.put("x-message-ttl",5000);//5秒后消息过期
        //parameter1:配置死信队列属性,parameter2:创建好的死信队列
        map.put("x-dead-letter-exchange","Dead_Exchange");//交换机过期的队列去到另一个交互机去
        map.put("x-dead-letter-routing-key","dead");//队列中的路由key
        return new Queue("ttl.direct.queue",true,false,false,map);
    }
    @Bean
    public Binding ttlDirectsmsBinding(){
        return BindingBuilder.bind(ttlDirectsmsQueue()).to(ttlDirectExchange()).with("ttl");
    }
}

一般都是ttl队列+死信队列+消费者配合用

12、高级确认机制

1、导入依赖
<dependencies>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
2、配置文件
spring:
  rabbitmq:
    host: 192.168.132.128
    username: admin
    password: admin
    publisher-returns: true
    listener:
      simple:
        concurrency: 10 #消费者数量
        max-concurrency: 10 #最大消费者的数量
        prefetch: 1 #限流(消费者每次从队列获取的消息数量)
        auto-startup: true  #启动时自动启动容器
        acknowledge-mode: manual #手动ack
    publisher-confirm-type: correlated   # 开启确认机制/老版 publisher-confirms: true
    virtual-host: /
    port: 5672
3、配置类
package edu.hunnan.rabbitmq.config;

import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.transaction.RabbitTransactionManager;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class RabbitConfig {

    /**
     * 定义消息转换实例 ,转化成 JSON传输
     *
     * @return Jackson2JsonMessageConverter
     */
    @Bean
    public MessageConverter integrationEventMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 配置启用rabbitmq事务
     *
     * @param connectionFactory connectionFactory
     * @return RabbitTransactionManager
     */
    @Bean
    public RabbitTransactionManager rabbitTransactionManager(CachingConnectionFactory connectionFactory) {
        return new RabbitTransactionManager(connectionFactory);
    }
}
4、初始化回调方法
package edu.hunnan.rabbitmq.service;

import edu.hunnan.rabbitmq.controller.RabbitController;
import org.mybatis.logging.Logger;
import org.mybatis.logging.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Service
public class RabbitMQService {
    @Autowired
    RabbitTemplate rabbitTemplate;

    private static final Logger LOGGER = LoggerFactory.getLogger(RabbitController.class);


    /**
     * 被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。
     */
    @PostConstruct
    private void init(){
        /**
         * 消息发送到交换器Exchange后触发回调。
         * 使用该功能需要开启确认,spring-boot中配置如下:
         * spring.rabbitmq.publisher-confirms = true
         */
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if (b) {
                    System.out.println("消息已确认 cause:{"+s+"} - {"+correlationData+"}");
                } else {
                    System.out.println("消息未确认 cause:{"+s+"} - {"+correlationData+"}");
                }
            }
        });
        /**
         * 通过实现ReturnCallback接口,
         * 如果消息从交换器发送到对应队列失败时触发
         * 比如根据发送消息时指定的routingKey找不到队列时会触发
         * 使用该功能需要开启确认,spring-boot中配置如下:
         * spring.rabbitmq.publisher-returns = true
         */
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.err.println("消息被退回"+message);
            }
        });
    }

}
5、测试类
package edu.hunnan.rabbitmq.controller;

import edu.hunnan.rabbitmq.dao.GoodsDao;
import edu.hunnan.rabbitmq.pojo.Goods;
import edu.hunnan.rabbitmq.pojo.Product;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RabbitController {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private GoodsDao goodsDao;

    private int userId=0;
    //开始抢单
    @RequestMapping("/begin")
    public void begin(){
        userId++;
        this.send(userId);
    }

    @RequestMapping("/send")
    public String send(Integer messge){
        //第一个参数:交换机名字  第二个参数:Routing Key的值  第三个参数:传递的消息对象
        rabbitTemplate.convertAndSend("seckill_exchange","seckill",messge);
        return "发送消息成功";
    }

    public void robbingProduct(Integer userId){
        Goods product = goodsDao.selectById("手机");
        if (product != null && product.getCount() > 0) {
            //更新库存表,库存量减少1。返回1说明更新成功。返回0说明库存已经为0
            boolean updata = goodsDao.updata("手机");
            if(updata){
                //插入记录
                goodsDao.inset(new Product("手机",userId));
                //发送短信
                System.err.println("用户抢单成功"+userId);
            }else {
                System.err.println("用户抢单失败"+userId);
            }
        } else {
            System.err.println("用户抢单失败"+userId);
        }

    }
    @GetMapping("/test")
    public void test(){
        Goods goods = goodsDao.selectById("手机");
        System.err.println(goods);
        goodsDao.inset(new Product("手机",1));
        goodsDao.updata("手机");
    }
}
6、配置监听方法(消费者)
package edu.hunnan.rabbitmq.consumer;

import com.rabbitmq.client.Channel;
import edu.hunnan.rabbitmq.controller.RabbitController;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.io.IOException;
@Component
public class Consumer {
    @Autowired
    RabbitController controller;

    /**
     * @RabbitListener 可以标注在类上面,需配合 @RabbitHandler 注解一起使用
     * @RabbitListener 标注在类上面表示当有收到消息的时候,就交给 @RabbitHandler 的方法处理,具体使用哪个方法处理,
     * 根据 MessageConverter 转换后的参数类型
     *
     * 使用 @Payload 和 @Headers 注解可以消息中的 body 与 headers 信息
     *
     * 通过 ACK 确认是否被正确接收,每个 Message 都要被确认(acknowledged),可以手动去 ACK 或自动 ACK
     */
    @RabbitListener(queues = "seckill.queue") //指定监听的队列名
    public void receiver(@Payload Integer userId, @Headers Channel channel, Message message) throws IOException {
        System.err.println("用户开始抢单"+userId);
        try {
            //处理消息
            controller.robbingProduct(userId);
//           确认消息已经消费成功
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            System.err.println("消费处理异常"+userId+"\t"+e);
//             拒绝当前消息,并把消息返回原队列 都设置false表示拒绝重发
                   /**
             * 接收失败设置重试  拒绝当前消息,并把消息返回原队列 都设置false表示拒绝重发 消息则丢失
             * 方式1 使用自带的重试机制并且控制重试次数  重试机制不能用try/catch否则会死循环 而是将异常抛出
             * 触发重试机制需要消费者抛出异常,而不能try/catch捕捉异常,不然会死循环。
             * 方式2 将接收失败的消息使用死信队列接盘   死信队列+try/catch
             */
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }
}

7、小结

以上案例是模拟用户抢单操作

rabbitmq队列是先进先出的顺序,先来后到,1000个请求你也得给我排队,前100个请求抢单成功之后就注定了后900个请求是抢单失败的!

使用RabbitMQ的最主要变化就是:以前抢单操作请求直接由我们抢单应用程序执行,现在请求被转移到了RabbitMQ服务器中。RabbitMQ服务器把接收到的抢单请求进行排队,最后由RabbitMQ服务器把抢单请求转发到我们的抢单应用程序,这样的好处就是避免我们的抢单应用程序短时间直接处理大量请求。RabbitMQ服务器主要作用是减缓抢单应用程序的并发压力,相当于在我们的抢单程序之前加了一道请求缓冲区。

8、使用场景
  • 消息投递一致性
  • 消息不丢失
  • 分布式事务
  • 流量削峰
9、重试机制配置
server:
  port: 7001

#RabbitMQ
spring:
  rabbitmq:
    username: admin
    password: admin
    host: 192.168.132.128
    port: 5672
    virtual-host: /
    publisher-returns: true
    listener:
      simple:
        concurrency: 10 #消费者数量
        max-concurrency: 10 #最大消费者的数量
        prefetch: 1 #限流(消费者每次从队列获取的消息数量)
        auto-startup: true  #启动时自动启动容器
        acknowledge-mode: manual #手动ack
        retry:
          enabled: true
          max-attempts: 3 # 重试次数
          max-interval: 10000   # 重试最大间隔时间
          initial-interval: 2000  # 重试初始间隔时间
          multiplier: 2 # 间隔时间乘子,间隔时间*乘子=下一次的间隔时间,最大不能超过设置的最大间隔时间
    publisher-confirm-type: correlated   # 开启确认机制/老版 publisher-confirms: true

不建议使用此方式 推荐是用死信队列+try/catch

13、分布式事务

14、延时队列

使用场景:淘宝7天自动确认收货,12306订单15分钟之内未支付等等…

1、安装地址

https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases

2、文件放到Linux文件下

rabbitmq安装目录plugins下

我的是 /usr/lib/rabbitmq/lib/rabbitmq_server-3.7.18/plugins

3、执行命令

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

4、重启rabbitmq

rabbitmqctl stop

rabbitmq-server -detached

5、启动rabbitmq

浏览器访问有选中的交换机则安装成功
在这里插入图片描述

6、整合SpringBoot
1、依赖
    <dependencies>
        <!--SpringBoot-->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.6.4</version>
        </dependency>
        <!--RabbitMQ-->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.7.7</version>
        </dependency>
        <!--Lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>
    </dependencies>
2、yaml
server:
  port: 5672

spring:
  application:
    name: hotel-rabbitmq
  rabbitmq:
    username: admin
    password: admin
    host: 192.168.132.128
    port: 5672
    virtual-host: /
    listener:
      simple:
        #手动确认机制
        acknowledge-mode: manual
    #开启true确认消息
    publisher-returns: true
    #消息发送到交换机上触发回调函数
    publisher-confirm-type: correlated
    template:
      mandatory: false #延时队列路由不生效解决方法
3、配置文件
package edu.hotel.rabbitmq.config;

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

import java.net.BindException;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class RabbitMQConfig {
    /**交换机名称**/
    public static final String LAZY_EXCHANGE ="LazyExchange";
    /**队列名称**/
    public static final String LAZY_Queue ="LazyQueue";
    /**routingKey*/
    public static final String LAZY_KEY ="10000";


    @Bean
    public CustomExchange customExchange(){
        Map<String, Object> map = new HashMap<>();
        //设置交换机支持延迟消息推送
        map.put("x-delayed-type","direct");
        return new CustomExchange(LAZY_EXCHANGE,"x-delayed-message",true,false,map);
    }

    @Bean
    public Queue LazyQueue(){
        //测试设置不用持久化
        return new Queue(LAZY_Queue,false);
    }

    @Bean
    public Binding LazyBinding(){
        return BindingBuilder.bind(LazyQueue()).to(customExchange()).with(LAZY_KEY).noargs();
    }
}

4、生产者
package edu.hotel.rabbitmq.service.impl;

import edu.hotel.rabbitmq.service.RabbitMQService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.text.SimpleDateFormat;
import java.util.Date;

@Service
@Slf4j
public class RabbitMQServiceImpl implements RabbitMQService {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init(){
        /**
         * 消息发送到交换机成功回调函数
         */
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean b, String s) {
                if (b){
                    log.info("消息投递到交换机成功");
                }else {
                    log.error("消息投递到交换机失败,原因->{}",s);
                }
            }
        });
        /**交换机投递到队列失败回调函数**/
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
                log.error("投递到队列失败,错误原因->{}",returnedMessage.getMessage());
            }
        });
    }



    @Override
    public Boolean sendLazyMessage(String value, String routinKey) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        log.info("消息发送时间->{}",simpleDateFormat.format(new Date()));
        rabbitTemplate.convertAndSend("LazyExchange", routinKey, value, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                Integer key  = Integer.parseInt(routinKey);
                log.info("延迟时间->{}",key);
                //这个底层就是setHeader("x-delay",i);是一样的 设置延时时间
                message.getMessageProperties().setDelay(5000);
                return message;
            }
        });
        return true;
    }
}

5、发送消息
   @GetMapping("/sendmessage")
    public String sendMessage(){
        rabbitMQService.sendLazyMessage("我是延迟消息","5000");
        return "发送成功";
    }

在这里插入图片描述

6、消费者
/**
 * 消费者
 */
@Slf4j
@Component
public class RabbitMQConsumer {
    @RabbitListener(queues = "LazyQueue")
    public void lazyListener(String message, Channel channel, Message message2) throws IOException, InterruptedException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        log.info("消息接收时间->{}",simpleDateFormat.format(new Date()));
        log.info("消息内容是->{}",message);
        log.info("{}",message2.getMessageProperties().getDeliveryTag());
        Thread.sleep(10000);
        channel.basicAck(message2.getMessageProperties().getDeliveryTag(),false);
    }
}

[外链图片转存中...(img-TK2Yz5CC-1692673015965)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值