RabbitMQ的使用1

RabbitMQ的使用

1.MQ是什么?

MQ:Message Queue :消息队列
队列:简单的说就是一种数据结构,先进先出,Redis(lpush+rpop)
消息队列:简单来说就是用来进行消息传输的
消息中间件:简单来说,就是用来传输消息的中间载体
中间件只有一个作用:就是将你的消息发送到接收方,他并不关心你发送的数据长啥样,就类似于生活中的快递员一样
而RabbitMQ就是这样的一个消息中间件

2.使用这个MQ能做什么?

2.1 流量消峰

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DgD9zaxj-1603889821176)(/1588041084061.png)]

如果有多个请求同时访问我们的服务器,利用消息中间件,将所有的并行请求串行化,然后再去访问咱们的服务器
如果一个队列有多个消费者,那么这些消费者的消费的数据之和才是队列中的所有数据

2.2 模块之间的异步通信

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jI3H1zWK-1603889821181)(/1588041290562.png)]

将消息放入到中间件中,所有的消费者都去消费这个消息,他们所消费的信息都是相同的

3.常见的消息中间件

ActiveMQ----JMS(sun公司提供的规范)java message Server
RabbitMQ
RocketMQ---- 阿里的
kafka ---用的比较多,最初的设计是用来完成分布式下日志的收集框架

4.RabbitMQ的安装

#1.安装之前需要的环境
yum install epel-release
yum install erlang
#2.安装RabbitMQ
下载rpm文件
 wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.15/rabbitmq-server-3.6.15-1.el7.noarch.rpm
#3.下载完成需要安装
yum install rabbitmq-server-3.6.15-1.el7.noarch.rpm
#4.设置开机启动
systemctl enable rabbitmq-server.service
#5.查看服务的状态
systemctl status rabbitmq-server.service
#6.启动这个服务
systemctl start rabbitmq-server.service
#7.查看当前所有的用户
rabbitmqctl list_users
#8.查看guest用户所有拥有的权限
  rabbitmqctl list_user_permissions guest
#9.删除原来的guest用户
  rabbitmqctl delete_user guest
#10.添加一个新的用户
  rabbitmqctl add_user wangzi 12345678
#11.给wangzi设置个角色(tag)
   rabbitmqctl set_user_tags wangzi administrator
#12.给wangzi赋予权限
   rabbitmqctl set_permissions -p / wangzi ".*" ".*" ".*"
#查看用户所拥有的权限
   rabbitmqctl list_user_permissions wangzi   
#开启web的管理端
rabbitmq-plugins enable rabbitmq_management (运行两次)
#查看端口
netstat -apn | grep 5672

5.RabbitMQ中的五种通信模型

5.1 Helloworld模型

意思是:生产者将消息发送到队列,然后队列将这个消息发送给消费者

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UxBKcmg9-1603889821183)(/1588568786897.png)]

1. 导包
        <!--导入rabbitmq相关的包-->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.4.3</version>
        </dependency>
2.编写工具类
public class RabbitMQUtils {

    public static Connection  getConnection() throws IOException, TimeoutException {
//    申明链接工厂
        ConnectionFactory factory = new ConnectionFactory();
//    设置连接的主机(ip地址)
        factory.setHost("192.168.1.19");
//    设置虚拟机
        factory.setVirtualHost("/");
//    设置访问的用户名
        factory.setUsername("");
//    设置密码
        factory.setPassword("");
//    设置请求的端口
        factory.setPort(5672);
//    创建链接
        return factory.newConnection();
    }
}
3.生产者

public class Producter {
//    队列的名字
    private static final String QUEUE_NAME="helloworld";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//          1.获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建数据输出的通道
        Channel channel = connection.createChannel();
//        3.申明队列
        /**
         * 第一个参数的名字:队列的名字
         * 第二个参数:是否持久化,比如发送到队列的消息,如果没有持久化,重启队列之后,数据会丢失,队列都会被删除
         * 持久化以后,重启以后,数据还在
         * 第三个参数:是否排外:
         *          1. 链接关闭以后,这个队列是否自动删除 true:删除
         *          2.是否允许其他通道来进行访问这个数据
         * 第四个参数:是否允许自动删除 true:自动删除
         *          当最后一个链接断开的时候,这个时候是否允许删除这个队列
         * 第五个参数:申明队列的时候,要附带的一些参数
                */
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        4.发送数据到队列
        /**
         * 第一个参数:exchange 交换机 没有就设置为空值
         * 第二个参数:routingKey:路由的key,现在没有key,直接使用队列的名字
         * 第三个参数:发送到队列的时候,是否要带一些参数,没有带任何参数
         * 第四个参数:向队列发送的数据
         */
        channel.basicPublish("",QUEUE_NAME,null,"rabbitMQ通信模型之helloworld111".getBytes());
        channel.close();
        connection.close();

   }
}
4.消费者

public class Consumer {
    //    队列的名字
    private static final String QUEUE_NAME="helloworld";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        1.获取连链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建通道
        final Channel channel = connection.createChannel();
//        3.申明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        4.消费者的声明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            /**
             *
             * @param consumerTag :这个消息的唯一标记
             * @param envelope :信封:请求的消息的属性的一些封装
             * @param properties  队列的属性值 (前面队列带过来的值)
             * @param body :接受到的消息
             * @throws IOException
             */
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("接收到的消息是"+new String(body));
//                进行手动应答
                /**
                 * 第一个参数:自动应答的消息的标记
                 * 第二个参数:false:就相当于告诉队列收到消息了
                 */
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
//        5.绑定这个消费者
        /**
         * 第一个参数:队列的名字
         * 第二个参数:是否自动应答
         *  当消费者获取到这个消息以后,如果你设置了自动应答(给谁应答:给队列应答),如果设置为
         *  true,那么就会告诉队列收到了这个消息,队列就会自动将这个消息删除(防止消息重复消费)
         *  手动应答:当收到消息,你需要自己调用这个代码完成这个消费
         * 第三个参数:消费者的声明
         */
        channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
//      如果没有关闭链接,则消费者会一直等待消息

    }
}

5.2 work模型

一个生产者将消息发送到队列,有多个消费者消费消息,多个消费者消费的消息之和等于总的消息数据(流量消峰)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQaZaazN-1603889821185)(/1588568814947.png)]

3.生产者

public class Producer {
    private static final String QUEUE_NAME="work-queue";
    public static void main ( String[] args ) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.getConnection();
//        创建数据输出的通道
        Channel channel = connection.createChannel();
//        声明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        发送数据到队列
        for (int i = 0; i <100 ; i++) {
            channel.basicPublish("",QUEUE_NAME,null,("work"+i).getBytes());
        }
        channel.close();
        connection.close();
    }
}

4.多个消费者
public class Consumer01 {
    private static final String QUEUE_NAME = "work-queue";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        获取通道
        Channel channel = connection.createChannel();
//        队列声明
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        消费者声明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println(new String(body));
            }
        };
//        绑定消费者
        channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
    }
}



/**
 * @Author 
 * @Date 
 */
public class Consumer02 {
    private static final String QUEUE_NAME="work-queue";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        获取数据传输的通道
        Channel channel = connection.createChannel();
//        对列声明
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        消费者的声明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println(new String(body));
            }
        };
//        绑定这个消费者
        channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
    }
}

5.3 发布订阅模型

简单的说,就是队列里面的消息会被几个消费者同时接受到,每个消费者接收到的数据都是相同的,都是生产者的全部消息

模型:适合做模块之间的异步通信

举例: 缓存同步,高并发下的单逻辑

1. 生产者
/**
 * @Author
 * @Date 
 */
public class producter {
    private static final String EXCHANGE_NAME = "publish-fanout";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        获取通道
        Channel channel = connection.createChannel();
//          交换机的声明
        /**
         * 第一个参数:交换机的名字
         * 第二个参数:交换机的类型 如果是发布订阅模式,只能写fanout
         */
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//      将消息发送到交换机
        for (int i = 0; i <100; i++) {
            channel.basicPublish(EXCHANGE_NAME,"",null,("publish-fanout"+i).getBytes());
        }
        channel.close();
        connection.close();
    }
}
2. 多个消费者
/**
 * @Author 
 * @Date 
 */
public class consumer01 {
    private static final String QUEUE_NAME01 = "queue01";
    private static final String EXCHANGE_NAME = "publish-fanout";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        获取通道
        Channel channel = connection.createChannel();
//        申明队列
        channel.queueDeclare(QUEUE_NAME01,false,false,false,null);
//        申明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//        将队列绑定到交换机
        /**
         * 第一个参数:队列的名字
         * 第二个参数:交换机的名字
         * 第三个参数:路由的key
         */
        channel.queueBind(QUEUE_NAME01,EXCHANGE_NAME,"");
//        消费者申明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("队列一的消息"+":"+new String(body));
            }
        };
//        绑定消费者
        channel.basicConsume(QUEUE_NAME01,true,defaultConsumer);
    }
}


/**
 * @Author 
 * @Date 2020/5/4 
 */
public class consumer02 {
    private static final String QUEUE_NAME02 = "queue02";
    private static final String EXCHANGE_NAME = "publish-fanout";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        获取通道
        Channel channel = connection.createChannel();
//        申明队列
        channel.queueDeclare(QUEUE_NAME02,false,false,false,null);
//        申明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"fanout");
//        将队列绑定到交换机
        /**
         * 第一个参数:队列的名字
         * 第二个参数:交换机的名字
         * 第三个参数:路由的key
         */
        channel.queueBind(QUEUE_NAME02,EXCHANGE_NAME,"");
//        消费者申明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("队列二的消息"+":"+new String(body));
            }
        };
//        绑定消费者
        channel.basicConsume(QUEUE_NAME02,true,defaultConsumer);
    }
}

5.4 路由模型

通过不同的路由,接受到不同的消息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iD4nytnt-1603889821187)(/1588568864604.png)]

1. 生产者
/**
 * @Author
 * @Date 
 */
public class Producter {
    private static final String EXCHANGE_NAME = "routing-direct";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        获取通道
        Channel channel = connection.createChannel();
//        如果是路由模型,交换机的类型的只能是direct
        channel.exchangeDeclare(EXCHANGE_NAME,"direct");
//        发送信息到交换机
        for (int i = 0; i <100 ; i++) {
//            路由的键是可以随便设置的
            channel.basicPublish(EXCHANGE_NAME,"blank",null,("路由模型的值"+i).getBytes());
            channel.basicPublish(EXCHANGE_NAME,"wangzi",null,"nihao".getBytes());
        }
        channel.close();
        connection.close();

    }
}
2. 多个消费者
/**
 * @Author 
 * @Date 
 */
public class Consumer01 {
    private static final String QUEUE_NAME= "route-queue001";
    private static final String EXCHANGE_NAME = "routing-direct";
    public static void main ( String[] args ) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        channel.exchangeDeclare(EXCHANGE_NAME,"direct");
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"blank");
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("队列一接受到的消息"+ new String(body));
            }
        };
        channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
    }
}


/**
 * @Author 
 * @Date 
 */
public class Consumer02 {
    private static final String QUEUE_NAME= "route-queue002";
    private static final String EXCHANGE_NAME = "routing-direct";
    public static void main ( String[] args ) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        channel.exchangeDeclare(EXCHANGE_NAME,"direct");
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"wangzi");
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("队列二接受到的消息"+ new String(body));
            }
        };
        channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
    }
}

5.5 topic模式

topic模式相当于是对路由模型的一个升级,topic模式主要就是在匹配的规则上可以模糊匹配
# 匹配一个或多个词
* 匹配一个词
eg: user.* 能够匹配到user.add
	user.# 能够匹配到user.add.delete

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UfaKCeUY-1603889821188)(/1588569138350.png)]

1. 生产者的写法

/**
 * @Author 
 * @Date 
 */
public class Producter {
    private static final String EXCHANGE_NAME = "routing-topic";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        获取通道
        Channel channel = connection.createChannel();
//        如果是路由模型,交换机的类型的只能是direct
        channel.exchangeDeclare(EXCHANGE_NAME,"topic");
//        发送信息到交换机
        for (int i = 0; i <100 ; i++) {
//            路由的键是可以随便设置的
//            如果使用了交换机,发送完数据,没有消费者的话,数据会丢失,所以需要持久化MessageProperties.PERSISTENT_TEXT_PLAIN
            channel.basicPublish(EXCHANGE_NAME,"blank.blank", MessageProperties.PERSISTENT_TEXT_PLAIN,("路由模型的值"+i).getBytes());
            channel.basicPublish(EXCHANGE_NAME,"wangzi.wang.zi",MessageProperties.PERSISTENT_TEXT_PLAIN,"nihao".getBytes());
        }
        channel.close();
        connection.close();

    }
}
2.多个消费者
/**
 * @Author 
 * @Date 
 */
public class Consumer01 {
    private static final String QUEUE_NAME= "topic-queue001";
    private static final String EXCHANGE_NAME = "routing-topic";
    public static void main ( String[] args ) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        channel.exchangeDeclare(EXCHANGE_NAME,"topic");
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"blank.*");
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("队列一接受到的消息"+ new String(body));
            }
        };
        channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
    }
}


/**
 * @Author 
 * @Date 
 */
public class Consumer02 {
    private static final String QUEUE_NAME= "topic-queue002";
    private static final String EXCHANGE_NAME = "routing-topic";
    public static void main ( String[] args ) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        channel.exchangeDeclare(EXCHANGE_NAME,"topic");
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"wangzi.#");
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("队列二接受到的消息"+ new String(body));
            }
        };
        channel.basicConsume(QUEUE_NAME,true,defaultConsumer);
    }
}

5.6 通信模型的备注

 channel.queueDeclare(QUEUE_NAME,false,false,false,null);
 queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments) 
durable :是否持久化,准确的说,持久化的是队列,并不是里面的数据  
如果要数据持久化的话应该这么设置:MessageProperties.PERSISTENT_TEXT_PLAIN
channel.basicPublish(EXCHANGE_NAME,"wangzi.wang.zi",MessageProperties.PERSISTENT_TEXT_PLAIN,"nihao".getBytes());

6.RabbitMQ的一些高级属性

6.0 参数的含义

持久化: 如果不持久化,那么RabbitMQ重启以后,数据会丢失,队列都会被删除
是否排外:链接一旦关闭,就会删除这个队列  channel.close();  connection.close();
是否自动删除:当最后一个消费者的链接断开的时候,这个队列自动删除

6.1 confirm机制

问题:放到队列中的消息,怎么保证就一定是成功的放入了队列

引入了confirm机制,这个机制的意思就是,只要消息放到queue是成功的,那么这个队列就一定会进行反馈

1.生产者的编写
public class Producter {
//    队列的名字
    private static final String QUEUE_NAME="helloworld-confirm";
    public static void main ( String[] args ) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
//        开启confirm消息确认
        channel.confirmSelect();
//        对消息的可能性进行实时监听
//        对消息的签收情况进行确认
        channel.addConfirmListener(new ConfirmListener() {
            @Override
            public void handleAck ( long l, boolean b ) throws IOException {
                System.out.println("发送成功的监听。。");
            }

            @Override
            public void handleNack ( long l, boolean b ) throws IOException {
                System.out.println("发送失败的监听。。");
            }
        });

        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        channel.basicPublish("",QUEUE_NAME,null,"rabbitMQ通信模型之helloworld111".getBytes());
   }
}
2.消费者的编写
public class Consumer {
    //    队列的名字
    private static final String QUEUE_NAME="helloworld-confirm";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        1.获取连链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建通道
        final Channel channel = connection.createChannel();
//        3.申明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        4.消费者的声明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            /**
             *
             * @param consumerTag :这个消息的唯一标记
             * @param envelope :信封:请求的消息的属性的一些封装
             * @param properties  队列的属性值 (前面队列带过来的值)
             * @param body :接受到的消息
             * @throws IOException
             */
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("接收到的消息是"+new String(body));
//                进行手动应答
                /**
                 * 第一个参数:自动应答的消息的标记
                 * 第二个参数:false:就相当于告诉队列收到消息了
                 */
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
//        5.绑定这个消费者
        /**
         * 第一个参数:队列的名字
         * 第二个参数:是否自动应答
         *  当消费者获取到这个消息以后,如果你设置了自动应答(给谁应答:给队列应答),如果设置为
         *  true,那么就会告诉队列收到了这个消息,队列就会自动将这个消息删除(防止消息重复消费)
         *  手动应答:当收到消息,你需要自己调用这个代码完成这个消费
         * 第三个参数:消费者的声明
         */
        channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
//      如果没有关闭链接,则消费者会一直等待消息

    }
}

6.2 return机制

场景:我们在发送消息的时候,我们指定的交换机不存在,或者指定的路由的key不存在,这种时候,我们需要监听着各种不可达的消息。这个时候,return机制就产生了

前提:必须有消费者存在

mandatory:如果设置为ture:就表示的是要监听不可达的消息 进行处理
如果设置为false  那么队列端会直接删除这个消息
1. 生产者的编写
/**
 * @Author 
 * @Date 2020/5/4 
 */
public class Produter {
    private static final String EXCAHNGE_NAME = "return";
    private static final String ROUTE_KEY="return.wangzi";
    private static final String ERROR_KEY = "abc.zi";
    public static void main ( String[] args ) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
//        添加return监听

        channel.addReturnListener(new ReturnListener() {
            /**
             *
             * @param i :队列相响应给浏览器的状态码
             * @param s :表示的状态码对应的文本信息
             * @param s1 :交换机的名字
             * @param s2 :路由的key
             * @param basicProperties :消息的属性
             * @param bytes :消息体的内容
             * @throws IOException
             */
            @Override
            public void handleReturn ( int i, String s, String s1, String s2, AMQP.BasicProperties basicProperties, byte[] bytes ) throws IOException {
                System.out.println("监听到不可达的消息");
                System.out.println(s2);
            }
        });
        channel.exchangeDeclare(EXCAHNGE_NAME,"topic");
        channel.basicPublish(EXCAHNGE_NAME,ERROR_KEY,true,null,"测试消息".getBytes());
    }
}
2.消费者的编写
/**
 * @Author
 * @Date 
 */
public class Consumer {
    private static final String EXCAHNGE_NAME = "return";
    private static final String ROUTE_KEY="return.#";
    private static final String QUEUE_NAME = "return_queue";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        获取通道
        Channel channel = connection.createChannel();
//        声明队列
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);
//        声明交换机
        channel.exchangeDeclare(EXCAHNGE_NAME,"topic");
//         绑定
        channel.queueBind(QUEUE_NAME,EXCAHNGE_NAME,ROUTE_KEY);
//        声明消费者
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("收到这个消息了");
            }
        };
        channel.basicConsume(QUEUE_NAME,true,defaultConsumer);

    }
}

6.3 消费端的限流

场景:消费者死了,队列里面一瞬间就积累了上万条数据,当我们再打开客户端的时候,瞬间就有巨量的信息推送过来,但是我们的客户端是没有办法同时处理这么多数据的,结果就是消费者又死了

这种场景下,我们就需要对消费端进行限流

1生产者的编写
public class Producter {
//    队列的名字
    private static final String QUEUE_NAME="limit-helloworld";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//          1.获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建数据输出的通道
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        for (int i = 0; i <100 ; i++) {
            channel.basicPublish("",QUEUE_NAME,null,("helloworld"+i).getBytes());
        }
        channel.close();
        connection.close();
   }
}
2. 消费者的编写
public class Consumer {
    //    队列的名字
    private static final String QUEUE_NAME="limit-helloworld";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        1.获取连链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建通道
        final Channel channel = connection.createChannel();
//        3.申明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        限流机制
        /**
         * 第一个参数:消息本身的大小如果设置为0,那么表示对消息本身的大小不限制
         * 第二个参数:告诉RabbitMQ不要一次性给消费者大于n个的消息,你要推送的前提是
         * 现在这n个消息已经手动确认
         * 第三个参数:true或者false,是否将上面的设置,应用于整个通道
         * true:整个通道里面都是这个策略
         * false:当前的Consumer是这个策略
         */
        channel.basicQos(0,5,false);
//        如果不设置的话,这个任务一开始就分配好了,还有必须手动确认消息是否完成
//        4.消费者的声明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {

            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("接收到的消息是"+new String(body));

                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
//      如果没有关闭链接,则消费者会一直等待消息

    }
}

// 第二个消费者

public class Consumer02 {
    //    队列的名字
    private static final String QUEUE_NAME="limit-helloworld";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        1.获取连链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建通道
        final Channel channel = connection.createChannel();
//        3.申明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        4.消费者的声明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {

            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("接收到的消息是"+new String(body));
//                进行手动应答
                /**
                 * 第一个参数:自动应答的消息的标记
                 * 第二个参数:false:就相当于告诉队列收到消息了
                 */
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
//      如果没有关闭链接,则消费者会一直等待消息

    }
}

6.4 TTL(Time to Live)队列

场景:我要下单,下单之后在一定的时间内,我的订单如果没有被处理,那么就会自动失效

备注:简单的说,就是咱们的队列中的消息是有时间限制的,如果超时那么这个消息会被队列给删除

1. 生产者的编写
public class Producter {
//    队列的名字
    private static final String QUEUE_NAME="ttl-helloworld";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//          1.获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建数据输出的通道
        Channel channel = connection.createChannel();
//        只要给下面的队列设置好属性,那么就变成ttl队列
        Map<String,Object> map = new HashMap();
      // 如果5s的时间内,没有消费者消费,那么这个数据就会失效
        map.put("x-message-ttl",5000);
        channel.queueDeclare(QUEUE_NAME,false,false,false,map);
        for (int i = 0; i <100 ; i++) {
            channel.basicPublish("",QUEUE_NAME,null,("helloworld"+i).getBytes());
        }
        channel.close();
        connection.close();
   }
}
2.消费者的编写
public class Consumer02 {
    //    队列的名字
    private static final String QUEUE_NAME="ttl-helloworld";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        1.获取连链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建通道
        final Channel channel = connection.createChannel();
//        3.申明队列
        Map<String,Object> map = new HashMap();
        map.put("x-message-ttl",5000);
        channel.queueDeclare(QUEUE_NAME,false,false,false,map);
//        4.消费者的声明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {

            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("接收到的消息是"+new String(body));
//                进行手动应答
                /**
                 * 第一个参数:自动应答的消息的标记
                 * 第二个参数:false:就相当于告诉队列收到消息了
                 */
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
//      如果没有关闭链接,则消费者会一直等待消息

    }
}

6.5 死信队列

什么是死信队列?

当消息在队列中变成死信以后,可以定义它重新push到另外一个交换机上,这个交换机也有自己对应的队列,这个队列被称为死信队列

死信:发送到队列中的消息被拒绝;消息的ttl时间过期;队列达到了最大长度,再往里面放信息

在满足上面死信的前提下,现在我们定义一个队列,这个队列专门处理死信

死信队列也是一个正常的交换机,和一般的交换机没有什么区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yRJKkQWo-1603889821189)(/1588596446943.png)]

1. 生产者的编写
/**
 * @Author 
 * @Date 
 * 生产者发消息是发送到了正常的交换机上面
 */
public class Producter {
//    定义正常的交换机
    private static final String NORMAL_EXCHANGE = "die_ttl_exchange";
//    路由的key
    private static final String ROUTE_KEY = "die.ttl";
    public static void main ( String[] args ) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        /**
         * 有一个正常的交换机用来发送消息,队列:正常情况下的队列(ttl队列)
         * 还得有一个队列,就是死信队列(过期之后,自动路由的队列)
         */
        for (int i = 0; i <5 ; i++) {
            channel.basicPublish(NORMAL_EXCHANGE,ROUTE_KEY,false,null,("nihao"+i).getBytes());
        }
    }
}
2.消费者的编写

public class Consumer {
//    正常的交换机
    private static final String NORMAL_EXCHANGE = "die_ttl_exchange";
//    定义死性队列交换机的名称
    private static final String ERROR_EXCHANGE = "die_exchange";
    private static final String ROUTE_KEY = "die.#";
//    正常的队列(ttl队列)
    private static final String QUEUE_NAME = "die_ttl_queue";
//    死性队列
    private static final String ERROR_QUEUE_NAME = "die_queue";

    public static void main ( String[] args ) throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.exchangeDeclare(NORMAL_EXCHANGE,"topic",true);
//        声明队列为ttl队列
        Map<String,Object> map = new HashMap();
        map.put("x-message-ttl",5000);
//        添加死性的属性
        map.put("x-dead-letter-exchange",ERROR_EXCHANGE);
        channel.queueDeclare(QUEUE_NAME,true,false,false,map);
        channel.queueBind(QUEUE_NAME,NORMAL_EXCHANGE,ROUTE_KEY);

//     
        channel.exchangeDeclare(ERROR_EXCHANGE,"topic");
//        声明死性队列
        channel.queueDeclare(ERROR_QUEUE_NAME,true,false,false,null);
//        进行死性队列的绑定
        channel.queueBind(ERROR_QUEUE_NAME,ERROR_EXCHANGE,"#");

        DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("获取到数据"+new String (body));
            }
        };
        channel.basicConsume(QUEUE_NAME,true ,defaultConsumer);
//        死性队列的监听
        channel.basicConsume(ERROR_QUEUE_NAME,true,defaultConsumer);
    }
}

6.6 消费者端手动签收和消息重回队列

场景:消费者端接收到了咱们队列中的数据,但是在进行业务逻辑处理的时候,发现一个问题,业务逻辑处理失败?怎么办?

手动签收应答,也可以手动拒绝,让消息重回队列

1. 生产者的编写
public class Producter {
//    队列的名字
    private static final String QUEUE_NAME="ack_helloworld";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//          1.获取链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建数据输出的通道
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        channel.basicPublish("",QUEUE_NAME,null,"rabbitMQ通信模型之helloworld111".getBytes());
        channel.close();
        connection.close();
   }
}
2.消费者的编写
public class Consumer {
    //    队列的名字
    private static final String QUEUE_NAME="ack_helloworld";
    public static void main ( String[] args ) throws IOException, TimeoutException {
//        1.获取连链接
        Connection connection = RabbitMQUtils.getConnection();
//        2.创建通道
        final Channel channel = connection.createChannel();
//        3.申明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
//        4.消费者的声明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {

            @Override
            public void handleDelivery ( String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body ) throws IOException {
                System.out.println("接收到的消息是"+new String(body));
//                进行手动应答
                /**
                 * 第一个参数:自动应答的消息的标记
                 * 第二个参数:false:就相当于告诉队列收到消息了
                 */
//                channel.basicAck(envelope.getDeliveryTag(),false);
//                也可以拒绝签收
//                第三个参数表示拒绝签收之后,是否重回队列
                channel.basicNack(envelope.getDeliveryTag(),false,true);
            }
        };
        channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
//      如果没有关闭链接,则消费者会一直等待消息

    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值