RabbitMQ的使用(三):RabbitMQ中的一些高级属性

RabbitMQ中的一些高级属性

1、讲下参数的含义
channel.queueDeclare(QUEUE_NAME,true,false,true,null);
第二个参数:如果是false   重启之后 队列都没有了 数据也会丢失
第三个参数:连接一旦关闭  那么就会删除这个队列
第四个参数:就是最后一个消费者 退出去之后 那么这个队列是否自动删除
第五个参数:讲ttl队列的时候要专门讲

channel.basicPublish("",QUEUE_NAME,null,"我是小波波1111".getBytes());

第一个参数 :交换机
第二个参数:路由key
第三个参数:设置的队列的属性
第四个参数:值
2、confirm机制

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

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

public class Producer {

    //申明队列的名字
    private static final String QUEUE_NAME="nz1904-confirm-01";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.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,true,null);
        channel.basicPublish("",QUEUE_NAME,null,"我是小波波1111".getBytes());

    }
}
3、return机制

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

前提:当前的队列必须要有消费者存在

//有一个参数需要设置

mandatory  如果设置为ture:就表示的是要监听不可达的消息 进行处理
如果设置为false  那么队列端会直接删除这个消息
3.1、生产者的编写
public class Producer {

    private static final String EXCHANGE_NAME="test_return_exchange1";
    //是能路由的key
    private static final String ROUTING_KEY="return.save";
    //是不能路由的key
    private static final String ROUTING_ERROR_KEY="abc.save";
    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //添加监听
        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("状态码:"+i+"---文本信息:"+s+"---交换机名字:"+s1+"----路由的key:s2");
                System.out.println("监听到不可达的消息");
                System.out.println("监听到不可达的消息");
                System.out.println("监听到不可达的消息");
            }
        });
        channel.basicPublish(EXCHANGE_NAME,ROUTING_ERROR_KEY,true,null,"这里是测试Return机制".getBytes());

    }
3.2、消费者的编写
public class Consumer {

    private static final String EXCHANGE_NAME="test_return_exchange1";

    //是能路由的key
    private static final String ROUTING_KEY="return.#";
    //制定绑定的队列
    private static final String QUEUE_NAME="test_return_queue";

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        //申明队列
        channel.queueDeclare(QUEUE_NAME,true,false,false,null);
        //申明交换机
        channel.exchangeDeclare(EXCHANGE_NAME,"topic");
        //绑定
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,ROUTING_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);
    }

}
4、消费端的限流问题

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

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

4.1、生产者的编写
public class Producer {

    //申明队列的名字
    private static final String QUEUE_NAME="nz1904-limit-01";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.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,("我是小波波"+i).getBytes());
        }
        channel.close();
        connection.close();
    }
}
4.2、消费者1的编写
public class Consumer {

    //申明队列的名字
    private static final String QUEUE_NAME="nz1904-limit-01";

    public static void main(String[] args) throws IOException, TimeoutException {

        //获取连接
        Connection connection = ConnectionUtils.getConnection();

        //创建通道
        final 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("消费者1接受到的消息是:"+new String(body));
                //进行手动应答
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        //绑定这个消费者
        /**
         * 第一个参数:队列的名字
         * 第二个参数:是否自动应答
         * 第三个参数:消费者的申明
         */     channel.basicConsume(QUEUE_NAME,false,defaultConsumer);

    }
}
4.3、消费者2的编写
public class Consumer1 {

    //申明队列的名字
    private static final String QUEUE_NAME="nz1904-limit-01";

    public static void main(String[] args) throws IOException, TimeoutException {

        //获取连接
        Connection connection = ConnectionUtils.getConnection();

        //创建通道
        final Channel channel = connection.createChannel();

        //申明队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //设置限流机制
        /**
         * 第一个参数:消息本身的大小 如果设置为0  那么表示对消息本身的大小不限制
         * 第二个参数:告诉rabbitmq不要一次性给消费者推送大于N个消息 你要推送的前提是
         * 现在这N个消息 已经手动被确认 已经完成
         * 第三个参数:true/false :是否将上面的设置应用于整个通道  true :表示整个通道的消费者
         * 都是这个策略  如果是false表示的是 只有当前的consumer 是这个策略
         */
        channel.basicQos(0,5,false);
        //结论:实际上如果不设置的话 分配任务的事 一开始就分配好了
        //必须手动签收

        //消费者的申明
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者2接受到的消息是:"+new String(body));
                try {
                  Thread.sleep(200);
                }catch (Exception err){

                }
                //进行手动应答
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };

        //绑定这个消费者
        /**
         * 第一个参数:队列的名字
         * 第二个参数:是否自动应答
         * 第三个参数:消费者的申明
         */
       channel.basicConsume(QUEUE_NAME,false,defaultConsumer);

    }
}
5、TTL队列(Time To Live)

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

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

public class Producer {
    //申明队列的名字
    private static final String QUEUE_NAME="nz1904-ttl-01";

    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();

        //我们只需要给下面的队列设置好属性 那么这个队列 就自动变成 ttl队列了
        Map<String,Object> map=new HashMap<>();
        map.put("x-message-ttl",5000);
        channel.queueDeclare(QUEUE_NAME,false,false,false,map);

        channel.basicPublish("",QUEUE_NAME, null,("我是小波波").getBytes());
        channel.close();
        connection.close();
    }
}
6、死信队列

什么是死信队列

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

死信:

​ 发送到队列中的消息被拒绝了

​ 消息的ttl时间过期

​ 队列达到了最大长度 再往里面放信息

在满足上面死信的前提下 、现在我们可以定义一个队列 这个队列专门用来

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

当这个队列中如果有这个死信的时候、rabbitmq就会将这个消息自动发送到我们提前定义好的死信队列中去(简单的说就是路由到另外一个队列)

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

6.5.1、生产者的编写
public class Producer {
    //定义的是队列(正常的交换机)  这里发送消息是在交换机上面
    private static final String EXCHANGE_NAME="ttl-dlx-bobo-exchange";
    //定义一个路由的key
    private static final String  ROUTING_KEY="dlx.#";

    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        for (int i = 0; i <5 ; i++) {
             channel.basicPublish(EXCHANGE_NAME,ROUTING_KEY,false,null,("我是小波波"+i).getBytes());
        }
    }
}
6.5.2、消费者的编写
public class Consumer {

    //定义的是交换机
    private static final String EXCHANGE_NAME="ttl-dlx-bobo-exchange";
    //正常情况下的队列
    private static final String QUEUE_NAME="ttl-dlx-bobo-queue";

    //定义死信队列的交换机的名字
    private static final  String DLX_EXCHANGE_NAME="dlx-bobo-exchange";
    //死信队列的定义
    private static final  String DLX_QUEUE_NAME="dlx-bobo-queue";
    
    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtils.getConnection();
        final Channel channel = connection.createChannel();
        //创建交换机和队列进行绑定
        channel.exchangeDeclare(EXCHANGE_NAME,"topic",true);
        //队列的申明
        //我们要申明成死信队列
        Map<String,Object> map=new HashMap<>();
        map.put("x-message-ttl",5000);
        //添加一个死信的属性  //后面这个名字就是死信队列交换机的名字
        map.put("x-dead-letter-exchange",DLX_EXCHANGE_NAME);

        channel.queueDeclare(QUEUE_NAME,true,false,false,map);

        //进行队列和交换机进行绑定
        channel.queueBind(QUEUE_NAME,EXCHANGE_NAME,"dlx.#");

        //上面是正常的队列的申明
        //下面就是死信队列的申明
        channel.exchangeDeclare(DLX_EXCHANGE_NAME,"topic");
        //申明队列
        channel.queueDeclare(DLX_QUEUE_NAME,true,false,false,null);
        //绑定这个死信队列
        channel.queueBind(DLX_QUEUE_NAME,DLX_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(DLX_QUEUE_NAME,true,defaultConsumer);
        //现在有这个问题呀?
    } 
}

7、消费者端手动签收和消息的重回队列

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

手动签收应答、我们也可以手动拒绝、然后让消息重回队列

7.1、生产者的编写
public class Producer {
    //申明队列的名字
    private static final String QUEUE_NAME="nz1904-ack-02";
    public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {

        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
   channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        channel.basicPublish("",QUEUE_NAME, null,"我是小波波1111".getBytes());
        channel.close();
        connection.close();
    }
}
7.2、消费者的编写
public class Consumer {

    //申明队列的名字
    private static final String QUEUE_NAME="nz1904-ack-02";
    public static void main(String[] args) throws IOException, TimeoutException {

        Connection connection = ConnectionUtils.getConnection();

        final 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.basicAck(envelope.getDeliveryTag(),false);
                //下面也可以拒绝签收
                /**
                 * 第三个参数:表示决绝签收之后这个消息是否要重回队列?
                 */
                channel.basicNack(envelope.getDeliveryTag(),false,true);
            }
        };     channel.basicConsume(QUEUE_NAME,false,defaultConsumer);
    }
}

希望大家关注我一波,防止以后迷路,有需要的可以加我Q讨论互相学习java ,学习路线探讨,经验分享与java Q:2415773436

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RabbitMQ是一个开源的消息队列系统,用于在应用程序之间进行异步通信。它实现了AMQP(高级消息队列协议)并提供了可靠的消息传递机制。下面是RabbitMQ使用和开发案例: 1. 安装和配置RabbitMQ: - 下载并安装RabbitMQ,可以从官方网站下载对应操作系统的安装程序。 - 配置RabbitMQ,通常只需修改一些基本配置,如端口号和认证信息。 2. 使用RabbitMQ的基本概念: - Producer:发送消息的应用程序。 - Queue:存储消息的地方,Producer发送消息到Queue。 - Consumer:接收并处理消息的应用程序。 3. RabbitMQ的开发案例: - 发布/订阅模式: - 创建一个Exchange(交换机),Producer将消息发送到Exchange。 - 创建多个Queue并将其绑定到Exchange上。 - Consumer订阅Queue并接收消息。 - 工作队列模式: - 创建一个Queue,多个Consumer监听该Queue。 - Producer将消息发送到Queue,每个消息只能被一个Consumer处理。 - 路由模式: - 创建一个Direct Exchange,并将多个Queue绑定到该Exchange上,每个Queue使用不同的Routing Key。 - Producer根据Routing Key发送消息到Exchange。 - Consumer根据绑定的Routing Key接收消息。 - 主题模式: - 创建一个Topic Exchange,并将多个Queue绑定到该Exchange上,每个Queue使用不同的Routing Key模式。 - Producer根据Routing Key发送消息到Exchange。 - Consumer根据匹配的Routing Key接收消息。 4. 使用RabbitMQ与Spring Boot集成: - 添加RabbitMQ的依赖:在`pom.xml`文件添加`spring-boot-starter-amqp`依赖。 - 配置RabbitMQ连接信息:在`application.properties`(或`application.yml`)文件配置RabbitMQ相关属性。 - 创建消息发送者和接收者:使用`RabbitTemplate`发送和接收消息,使用`@RabbitListener`注解监听消息。 - 创建消息队列:使用`@RabbitListener`注解标记方法,该方法将作为消息队列的监听器。 以上是RabbitMQ的基本使用和开发案例。实际应用,还可以进行更复杂的配置和处理,如消息持久化、确认机制、消息传递模式等。可以参考RabbitMQ的官方文档和Spring Boot的官方文档以获取更详细的信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值