• Native RabbitMQ RejectMessage


    消费者在消费消息的时候,可以根据业务场景来选择拒绝消息,通过参数设置是否让消息再次入队。拒绝消息的指令有俩,一个是basicNack,一个是basicReject,这两个区别是后者不能批量拒绝,只能单条拒绝。代码演示如下:

    一个生产者,两个正常消费者,一个拒绝消息的消费者,三个消费者共同订阅一个队列,这样生产者发布的消息会轮询被三个消费者消费,演示代码如下:

    生产者

    /**
     * 普通的生产者
     *
     * @author zhangjianbing
     * time 2020/09/04
     */
    @SuppressWarnings("Duplicates")
    public class NormalProducer {
    
        public static final String EXCHANGE_NAME = "reject-exchange";
    
        public static void main(String[] args) throws Exception {
            // 创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("1.1.1.1");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("beijing");
            connectionFactory.setPassword("123456");
            connectionFactory.setVirtualHost("beijing");
            // 创建连接
            Connection connection = connectionFactory.newConnection();
            // 创建信道
            final Channel channel = connection.createChannel();
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT, false);
    
            String queue = "MESSAGE.CALLBACK.QUEUE";
            // 注意同时设置队列、消息持久化才显得有意义
            channel.queueDeclare(queue, false, false, false, null);
    
            String routeKey = "error";
            /** 将队列和交换器通过路由键绑定,表示此队列只接受error的消息 **/
            channel.queueBind(queue, EXCHANGE_NAME, routeKey);
    
            for (int i = 1; i < 11; i++) {
                String message = "hello_world_" + i;
                channel.basicPublish(EXCHANGE_NAME, routeKey, null, message.getBytes());
                System.out.println("生产者发送消息:" + message);
            }
            // 关闭信道和连接
            channel.close();
            connection.close();
        }
    
    }
    

    正常消费者A

    /**
     * 普通消费者A
     *
     * @author zhangjianbing
     * time 2020/09/04
     */
    @SuppressWarnings("Duplicates")
    public class NormalConsumerA {
    
        public static void main(String[] args) throws Exception {
            // 创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("10.1.3.37");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("beijing");
            connectionFactory.setPassword("123456");
            connectionFactory.setVirtualHost("beijing");
            // 创建连接
            Connection connection = connectionFactory.newConnection();
            // 创建信道
            final Channel channel = connection.createChannel();
    
            String queue = "MESSAGE.CALLBACK.QUEUE";
            System.out.println("正在等待消息。。。。。。");
    
            /**声明一个消费者**/
            final Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String message = new String(body, StandardCharsets.UTF_8);
                    System.out.println("NormalConsumerA收到的消息:" + message);
                    // false 表示单条确认 true 表示确认tag之前所有的消息
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            };
    
            /**手动确认模式**/
            channel.basicConsume(queue, false, consumer);
        }
    
    }
    

    正常消费者B

    /**
     * 普通消费者B
     *
     * @author zhangjianbing
     * time 2020/09/04
     */
    @SuppressWarnings("Duplicates")
    public class NormalConsumerB {
    
        public static void main(String[] args) throws Exception {
            // 创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("10.1.3.37");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("beijing");
            connectionFactory.setPassword("123456");
            connectionFactory.setVirtualHost("beijing");
            // 创建连接
            Connection connection = connectionFactory.newConnection();
            // 创建信道
            final Channel channel = connection.createChannel();
    
            String queue = "MESSAGE.CALLBACK.QUEUE";
            System.out.println("正在等待消息。。。。。。");
    
            /**声明一个消费者**/
            final Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String message = new String(body, StandardCharsets.UTF_8);
                    System.out.println("NormalConsumerB收到的消息:" + message);
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            };
    
            /**手动确认模式**/
            channel.basicConsume(queue, false, consumer);
        }
    
    }
    

    拒绝消费者C

    /**
     * 拒绝消费者
     *          当多个消费者共同消费同一个队列的时候
     *          此消费者会根据业务需求来拒绝消息,可以选择是否将消息重新入队
     *          重新入队的消息会被打上一个重新入队的标签
     *          重新入队的消息会放在队列的末尾,再次轮询投递给队列上的消费者
     *
     * 拒绝消息有两种方式:
     *          ① basicNack
     *          ② basicReject
     *          这俩唯一的不同就是basicNack有批量拒绝参数
     *
     * @author zhangjianbing
     * time 2020/09/04
     */
    @SuppressWarnings("Duplicates")
    public class RejectConsumer {
    
        public static void main(String[] args) throws Exception {
            // 创建连接工厂
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("10.1.3.37");
            connectionFactory.setPort(5672);
            connectionFactory.setUsername("beijing");
            connectionFactory.setPassword("123456");
            connectionFactory.setVirtualHost("beijing");
            // 创建连接
            Connection connection = connectionFactory.newConnection();
            // 创建信道
            final Channel channel = connection.createChannel();
    
            String queue = "MESSAGE.CALLBACK.QUEUE";
            System.out.println("正在等待消息。。。。。。");
    
            /**声明一个消费者**/
            final Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                    String message = new String(body, StandardCharsets.UTF_8);
                    System.out.println("RejectConsumer收到的消息:" + message);
                    try {
                        throw new RuntimeException("RejectConsumer业务处理发生异常");
                    } catch (Exception re) {
                        System.out.println(re.getMessage() + ",消息重新入队");
                        channel.basicReject(envelope.getDeliveryTag(), true);// 重新入队
                    }
                }
            };
    
            /**手动确认模式**/
            channel.basicConsume(queue, false, consumer);
        }
    
    }
    

    测试结果

    生产者发送10条消息,假设消息投递的顺序是A,B,C

    消费者A:

    正在等待消息。。。。。。
    NormalConsumerA收到的消息:hello_world_1
    NormalConsumerA收到的消息:hello_world_4
    NormalConsumerA收到的消息:hello_world_7
    NormalConsumerA收到的消息:hello_world_10
    NormalConsumerA收到的消息:hello_world_9
    

    消费者B:

    正在等待消息。。。。。。
    NormalConsumerB收到的消息:hello_world_2
    NormalConsumerB收到的消息:hello_world_5
    NormalConsumerB收到的消息:hello_world_8
    NormalConsumerB收到的消息:hello_world_3
    NormalConsumerB收到的消息:hello_world_6
    

    消费者C:

    正在等待消息。。。。。。
    RejectConsumer收到的消息:hello_world_3
    RejectConsumer业务处理发生异常,消息重新入队
    RejectConsumer收到的消息:hello_world_6
    RejectConsumer业务处理发生异常,消息重新入队
    RejectConsumer收到的消息:hello_world_9
    RejectConsumer业务处理发生异常,消息重新入队
    RejectConsumer收到的消息:hello_world_6
    RejectConsumer业务处理发生异常,消息重新入队
    

    消费者C的结果中显示消息6被拒绝了两次,说明被拒绝的消息重新入队后还可以再次被C消费,只是再拒绝一次。

    多个消费者共同消费一个队列,消息首先投递给谁,取决于哪个消费者先启动,这是轮询算法的问题。

  • 相关阅读:
    人工智能学习笔记003-Python运算符
    人工智能学习笔记002-Python数据类型
    人工智能学习笔记001—python介绍
    dataclasses 笔记
    Js逆向-滑动验证码图片还原
    python3 marshmallow学习
    python 安装 SQLAlchemy 报错
    flask 与 SQLAlchemy的使用
    flask 与 flask_migrate的使用
    flask与flask-script的使用
  • 原文地址:https://www.cnblogs.com/zhangjianbing/p/13618231.html
Copyright © 2020-2023  润新知