Bootstrap

Rabbitmq

初学rabbimt

在这里插入图片描述

下载安装

在这里插入图片描述

官网:  www.rabbitmq.com

1 因为他是其他语言写的,所以先安装支持他的语言的环境 

2 安装 进入该目录
               C:\Program Files\RabbitMQ Server\rabbitmq_server-3.8.3\sbin
运行入下:
	rabbitmq-plugins enable rabbitmq_management
-- 找到该目录 删除下面的文件
	C:\Users\admin\AppData\Roaming\RabbitMQ\db

-- 重新安装  
	访问 : http://localhost:15672/  用户名和密码  guest  guest

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

看代码

  //简单队列名称
    private static final String SIMPLE_QUEUE_NAME = "simple_mq";

    /*
     * 获取rabbitmq链接
     * rabbitmq是AMPQ协议 有 服务地址 及 端口号 及 访问的数据库 及 用户名+密码
     */
    public static Connection getConnection() throws Exception {
        ConnectionFactory connection = new ConnectionFactory();
        connection.setHost("192.168.0.50");
        connection.setPort(15276);
        connection.setVirtualHost("/simple_mq");//simple_mq 相当于数据库
        connection.setUsername("user");
        connection.setPassword("123");
        return connection.newConnection();
    }

    /**
     * 模式一:简单队列 simple P->C
     * 1 生产者通过通道生成消息
     * 2 创建队列(名称)
     * 3 消费者通过队列(名称)获取消息
     * <p>
     * 简单队列的不足:
     * 生产者与消费者一一对应(如果我想有多个消费者消费此消息的时候就明显的不租了)
     * 当生产者队列名称变了的时候消费者也需要变更,此时出现模式二了
     */
    @Test
    public void SendSms() throws Exception {
        Connection connection = null;
        Channel channel = null;
        try {
            //获取链接
            connection = getConnection();
            //根据链接创建一个通道
            channel = connection.createChannel();
            //根据通道声明队列
            channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
            //创建消息
            String msg = "hell simpleQueue";
            //发布消息
            channel.basicPublish("", SIMPLE_QUEUE_NAME, null, msg.getBytes());
            System.out.println("发送消息:" + msg);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            channel.close();
            connection.close();
        }
    }

    /**
     * 接受消息
     */
    @Test
    public void getSms() throws Exception {
        Connection connection = null;
        Channel channel = null;
        //获取链接
        connection = RabbitmqConnectionUtilsTest.getConnection();
        //根据链接创建一个通道
        channel = connection.createChannel();
        //队列声明
        channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);

        //构建消费者
        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println(msg);
            }
        };

        //监听队列
        channel.basicConsume(SIMPLE_QUEUE_NAME, true, consumer);
    }

    /**
     * |C1
     * 模式二: work queue 工作队列 (一对多) P---|.........Cn
     * |C2
     * <p>
     * 现象:
     * 消费者1 和 消费者2 所处理(接收)的消息数量是一样的
     * 消费者1 处理的是奇数的消息
     * 消费者2 处理的是偶数的消息
     * 这种方式也称作为轮训(Round-robin):结果是不管谁忙谁闲都是一人一个
     */
    private void workProduceSend() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
        //发送消息
        for (int i = 0; i < 50; i++) {
            String msg = "work queue" + i;
            System.out.println("发送消息:" + msg);
            //发布消息
            Thread.sleep(i * 20);
            channel.basicPublish("", SIMPLE_QUEUE_NAME, null, msg.getBytes());
        }
        channel.close();
        ;
        connection.close();
    }

    /**
     * 消费者1
     */
    private void workConsumer1() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
        Consumer consumer1 = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println("消费者1接收到的消息:" + msg);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //监听消息(队列)
        channel.basicConsume(SIMPLE_QUEUE_NAME, true, consumer1);
    }

    /**
     * 消费者2
     */
    private void workConsumer2() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
        Consumer consumer2 = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println("消费者2接收到的消息:" + msg);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        //监听队列消息
        channel.basicConsume(SIMPLE_QUEUE_NAME, true, consumer2);
    }

    /**
     * |C1
     * 模式二: work queue 工作队列 (一对多) P---|.........Cn
     * |C2
     * <p>
     * 现象:
     * 消费者1 和 消费者2 所处理(接收)的消息数量是一样的
     * 消费者1 处理的是奇数的消息
     * 消费者2 处理的是偶数的消息
     * 这种方式也称作为轮训(Round-robin):结果是不管谁忙谁闲都是一人一个,但是现在想让处理快的多处理一些,这个时候怎么处理呢?此时公平分发就出现了
     * <p>
     * 公平分发(Fair dispatch):当消费者消费完之后,手动告诉生产者,我消费完了,继续消费
     * 使用公平分发必须关闭自动应答 ack  改成手动应答 ,使用basicQos(perfetch=1)
     * <p>
     * boolean autoAck = true;自动应答模式
     * channel.basicConsume(SIMPLE_QUEUE_NAME,autoAck,consumer1);
     * 消息应答模式默认为false
     * boolean autoAck = true;自动应答模式,
     * 一旦rabbitmq将消息发送个消费之,消息将从内存中删除,一旦杀死正在执行的消费者,处理的数据将会丢失
     * boolean autoAck = false;手动应答模式
     * 一旦消费者挂了,就会交付给其他的消费者,rabbitmq支持消息应答,消费者发送一个消息,告诉rabbitmq,这个消息我已经处理完了,你可以删了
     * 然后rabbitmq就会把消息从内存中删掉
     * <p>
     * 综上:如果rabbitmq挂了 那么这个消息仍然会丢失?如果将数据存储到类似于数据库的地方,如磁盘上,你挂了就挂了,重启后读取不就好了吗.
     * rabbitmq的数据是支持持久化的
     * boolean durable = false;
     * channel.queueDeclare(SIMPLE_QUEUE_NAME,durable,false,false,null);
     * 在声明队列的时候,第二个参数的含义就是持久化的意思,设置为true即持久化,但是在代码当中不能直接修改一个已经存在的队列的 durable 为 true
     * 因为该队列已经存在,在创建的时候是未持久化的,如果你想持久化,在rabbitmq的控制面板直接删除该队列运行即可
     */
    private void fairProduceSend() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
        /**
         * 每个消费者确认消费之前,消息队列不发送下一个消息到消费者,一次只处理发送一条消息
         *
         * 限制发送给同一个消费者不得超过一条消息
         */
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);
        //发送消息
        for (int i = 0; i < 50; i++) {
            String msg = "work queue" + i;
            System.out.println("发送消息:" + msg);
            //发布消息
            Thread.sleep(i * 20);
            channel.basicPublish("", SIMPLE_QUEUE_NAME, null, msg.getBytes());
        }
        channel.close();
        ;
        connection.close();
    }

    /**
     * 消费者1
     * 每次只接受一个数据,并且接收完毕后给生成这确认反馈,并且改为手动应答
     */
    private void fairConsumer1() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        final Channel channel = connection.createChannel();
        /**
         * 每次只接收一条数据
         */
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);
        //声明队列
        channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
        Consumer consumer1 = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println("消费者1接收到的消息:" + msg);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //确认接收后,给消费之反馈恢复
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听消息(队列)
        /**
         * 设置自动应答,改为false
         */
        boolean autoAck = false;
        channel.basicConsume(SIMPLE_QUEUE_NAME, autoAck, consumer1);
    }

    /**
     * 消费者2
     */
    private void fairConsumer2() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        final Channel channel = connection.createChannel();
        /**
         * 确认每次只接收一条消息
         */
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);
        //声明队列
        channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
        Consumer consumer2 = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println("消费者2接收到的消息:" + msg);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //确认接收后给生成这回执
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听队列消息
        /**
         * 自动应答设置为false手动应答
         */
        boolean autoAck = false;
        channel.basicConsume(SIMPLE_QUEUE_NAME, autoAck, consumer2);
    }


    /***
     *                                              |----->队列 1 ------C1
     * 模式三 订阅模式(publish subscirbe)   P---X----|
     *                                             |------>队列 2 ------C2
     *
     *      P 生产者 :
     *              一个生产者对应多个消费者
     *              每个消费者都有自己的队列
     *      X 交换机 转换器 exchange:生产者没有直接把消息发送给队列,而是发给了交换机
     *      每个队列都要绑定在交换机上面
     *      生产者发送的消息,经过交换机转发到队列,就能够实现一个生产者呗多个消费者消费
     * 这就是订阅模式
     *         如果只有交换机,没有队列,那么执行代码没问题,但是,消息就不知道哪去了,消息丢失
     * 在 rabbitmq中 交换机是没有存储能力的,只有队列是有存储能力的,要想消息存在,那么就得交换机和队列绑定 消费者存在队列 直接绑定
     *
     */
    //定义一个交换机
    public static final String EXCHANGE_NAME = "exchange_fanout";

    //生产者
    public static void publishSend() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");//通过交换机分发 fanout(不处理路由键)
        String msg = "publish subscirbe";
        channel.basicPublish(EXCHANGE_NAME, "", null, msg.getBytes());
        System.out.println("Send:" + msg);
        channel.close();
        connection.close();
    }

    //消费之1
    public void publishRevice1() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
        //绑定交换机
        channel.queueBind(SIMPLE_QUEUE_NAME, EXCHANGE_NAME, "");
        //创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println("消费者1接收消息:" + msg);
            }
        };
        //监听队列
        channel.basicConsume(SIMPLE_QUEUE_NAME, false, consumer);

    }

    //消费之2
    public void publishRevice2() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(SIMPLE_QUEUE_NAME, false, false, false, null);
        //绑定交换机
        channel.queueBind(SIMPLE_QUEUE_NAME, EXCHANGE_NAME, "");
        //创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println("消费者2接收消息:" + msg);
            }
        };
        //监听队列
        channel.basicConsume(SIMPLE_QUEUE_NAME, false, consumer);

    }

    /****
     * 模式四:路由模式(routing)
     *
     *      在说路由模式之前先说一下交换机或者是转换器Exchange
     *    channel.exchangeDeclare(EXCHANGE_NAME,"fanout");//通过交换机分发 fanout(不处理路由键)
     *
     *fanout exchange:不处理路由键
     *    fanout Exchange :当消息发送过来后 通过交换机接收,然后只要是绑定了该交换机的队列,都会接收到消息,然后分发给各自的消费者
     *    不处理路由键发送消息的时候 路由键是空的  第二个参数 routingKey ="" channel.basicPublish(EXCHANGE_NAME,"",null,msg.getBytes());
     *
     *direct exchange: 处理路由键
     *
     *路由模式图:
     *                                              |------queue1(队列)----->C
     *                                         |(key=error)
     *                                    |
     *       P ----->X(type=direct)-    |
     *                                     |(key=info)
     *                                        |(key=waring)
     *                                           |(key=error)
     *                                              |-----queue2(队列-------C
     *
     *图解:
     *      生产者发送消息到交换机,此时设置交换机类型为 direct,
     *      当交换机所持有的key为error的时候,队列1和队列2 都可以接收到消息,然后给自己的消费者
     *      当key=info的时候,只有队列2能接收到消息进行存储发给自己的消费者
     *
     *
     */
    private static final String EXCHANGE_NAME_DIRECT = "exchange_direct";

    public void directExchangeSend() throws Exception {
        //P----X----QUEUE--C
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取通过
        Channel channel = connection.createChannel();
        //声明交换机,并设置交换机类型为路由键direct
        channel.exchangeDeclare(EXCHANGE_NAME_DIRECT, "direct");
        //设置消息
        String msg = "hello exchange_direct";
        System.out.println(msg);
        //设置路由键key
        String routingKey = "error";
        //发送消息
        channel.basicPublish(EXCHANGE_NAME_DIRECT, routingKey, null, msg.getBytes());
        channel.close();
        connection.close();
    }

    /**
     * 消费者 1
     */
    //队列1
    private static final String QUEUE1_DIRECT_NAME = "queue_direct1";
    private static final String QUEUE2_DIRECT_NAME = "queue_direct2";

    public void directReviceConsumer() throws Exception {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取通道
        final Channel channel = connection.createChannel();
        //声明队列
        boolean durable = false;//数据是否需要持久化
        channel.queueDeclare(QUEUE1_DIRECT_NAME, false, false, false, null);
        //绑定交换机,第三个参数是绑定路由键的key
        String routingKey = "error";
        channel.queueBind(QUEUE1_DIRECT_NAME, EXCHANGE_NAME_DIRECT, routingKey);
        //设置为公平分发,当消费者确认接收消息后再告诉发送者,然后发送者将数据从内存中删除,限制发给同一消费者同一时间不得超过一条消息
        int prefertchCount = 1;
        channel.basicQos(prefertchCount);
        //设置消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println("消费者1接收消息:" + msg);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //使用公平分发,确认接收后,给消费之反馈恢复
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听消费者
        boolean autoAck = false;//自动应答模式设置为false ,false为手动应答
        channel.basicConsume(QUEUE1_DIRECT_NAME, false, consumer);
    }

    /**
     * 消费者2
     */
    public void directReviceConsumer2() throws Exception {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取通道
        final Channel channel = connection.createChannel();
        //声明队列
        boolean durable = false;//设置数据是否需要持久化
        channel.queueDeclare(QUEUE2_DIRECT_NAME, durable, false, false, null);
        //绑定交换机
        //因为路由键有多个,所以这里绑定多个
        String routingKey1 = "error";
        String routingKey2 = "info";
        String routingKey3 = "waring";
        channel.queueBind(QUEUE2_DIRECT_NAME, EXCHANGE_NAME_DIRECT, routingKey1);
        channel.queueBind(QUEUE2_DIRECT_NAME, EXCHANGE_NAME_DIRECT, routingKey2);
        channel.queueBind(QUEUE2_DIRECT_NAME, EXCHANGE_NAME_DIRECT, routingKey3);
        //设置公平分发,每个消费者确认消费之前,消息队列不发送下一个消息到消费者,一次只处理发送一条消息
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);
        //设置消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                //获取到消息
                String msg = new String(body, "utf-8");
                System.out.println("消费者2接收到的消息:" + msg);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //手动回执,当消费者接收到消息后,告诉发送者,发送者将数据从内存冲删除
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听消费者
        //设置应答类型为false 因为使用公平分发,这里需要设置为false
        boolean autoAck = false;
        channel.basicConsume(QUEUE2_DIRECT_NAME, autoAck, consumer);
    }


    /***
     * 模式五 :主题模式(topic)
     *                   Topic Exchange :
     *                              将路由键和某种模式匹配
     *
     *模型图:
     *                            |(*.orange.*)----->QUEUE1------C
     *                           |
     *                          |
     *  P-------X (type=topic)--
     *                          |
     *                          |(*.*.rabbit)
     *                          |            ------QUEUE2-------C
     *                          | (lazy.#)
     *注释: * 匹配一个  # 匹配多个
     *由模型图可以看出  主题模式的交换机类型设定为 topic ,routingKey为自定义设置
     *
     *
     * 例子:
     *      商品:发布 修改 删除 查询
     */
    //创建一个交换机名称
    private static final String TOPIC_EXCHANGE_NAME = "topic_exchange";

    public void topicExchangeSend() throws Exception {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //创建通道
        Channel channel = connection.createChannel();
        //声明交换机
        channel.exchangeDeclare(TOPIC_EXCHANGE_NAME, "topic");
        //生成消息
        String msg = "hello topic";
        //设置路由键key
        String routingKey = "good.add";
        //发布消息
        channel.basicPublish(TOPIC_EXCHANGE_NAME, routingKey, null, msg.getBytes());
        channel.close();
        connection.close();
    }

    //创建队列的名称
    private static final String QUEUE_TOPIC1_NAME = "topic_queue_1";
    private static final String QUEUE_TOPIC2_NAME = "topic_queue_2";

    /**
     * 消费者1
     */
    public void topicReviceConsumer() throws Exception {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取通道
        final Channel channel = connection.createChannel();
        //声明队列
        boolean durable = false;//设置数据是否需要持久化
        channel.queueDeclare(QUEUE_TOPIC1_NAME, false, false, false, null);
        //绑定交换机
        String routingKey = "good.#";
        channel.queueBind(QUEUE_TOPIC1_NAME, TOPIC_EXCHANGE_NAME, routingKey);
        //设置公平分发,每个消费者确认消费之前,消息队列不发送下一个消息到消费者,一次只处理发送一条消息
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);
        //创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println("主题模式消费者1接收消息为:" + msg);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //设置回执,消费者确认接收到消息后告诉消费者
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听消费者
        //因为是公平分发,手动触发,所以设置自动回答为false
        boolean autoAck = false;
        channel.basicConsume(QUEUE_TOPIC1_NAME, autoAck, consumer);
    }

    /**
     * 消费者2
     */
    public void topicReviceConsumer2() throws Exception {
        //获取链接
        Connection connection = ConnectionUtils.getConnection();
        //获取通道
        final Channel channel = connection.createChannel();
        //声明队列
        boolean durable = false;//设置rabbitmq发送的数据是否需要持久化,因为他是支持持久化的
        channel.queueDeclare(QUEUE_TOPIC2_NAME, durable, false, false, null);
        //绑定交换机
        channel.exchangeBind(QUEUE_TOPIC2_NAME, TOPIC_EXCHANGE_NAME, "good.add");
        channel.exchangeBind(QUEUE_TOPIC2_NAME, TOPIC_EXCHANGE_NAME, "good.update");
        channel.exchangeBind(QUEUE_TOPIC2_NAME, TOPIC_EXCHANGE_NAME, "good.query");
        //设置公平分发,确认每个消费者同一时间内处理一条消息 ,消费者接收到消息确认后,再回执给发送者,然后发送者再清楚数据
        int prefetchCount = 1;
        channel.basicQos(prefetchCount);
        //创建消费者
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                //接收消息
                String msg = new String(body, "utf-8");
                System.out.println("主题模式消费者2接收到的消息为:" + msg);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //当消费者确认消息接收后,回执给发送者
                    channel.basicAck(envelope.getDeliveryTag(), false);
                }
            }
        };
        //监听消费者
        //回执应答设置为false (手动的)
        boolean autoAck = false;
        channel.basicConsume(QUEUE_TOPIC2_NAME, autoAck, consumer);
    }


    /***
     * Rabbitmq的消息确认机制(事务+confirm)
     *
     *  在rabbitmq中持久化数据可以解决rabbitmq异常导致数据丢失问题,在消费者中设置
     * 生产者将消息发送之后,数据到底有没有到rabbitmq服务器,默认情况是不知道的
     *
     * 两种方式:(都是对生产者设置)
     *
     *      AMQP 实现了事务机制(与mysql类似)
     *      Confirm 模式
     *
     * AMQP 事务机制 降低了rabbitmq的吞吐量
     *      txSelect txCommit txRollback
     *
     *      txSelect : 将当前channle设置成transion模式
     *      txCommit : 用于提交事务
     *      txRollback : 事务回滚
     *
     *Confirm 最大的好处在于它是异步的
     *
     *      开启confirm模式
     *      channel.confirmSelect();
     *
     * 编程模式
     *
     *      1 普通的 发送一条消息 waitForConfirms()
     *      2 批量的 发送多条消息 waitForComfirms()
     *      3 异步的confir模式提供一个回调函数
     *
     * confirm的异步模式 :
     *
     *          confirm对象提供的ConfirmListener()回调方法只包含deliveryTag
     *
     * deliveryTag : 生产者没发送一条消息,该调消息对应的序列号
     *
     *          我们需要为每一个Channel维护一个unconfirm(未接收的消息的集合)的结集合,每publish一条数据集合元素+1
     *   每回调一次handleAck方法,unconfirm集合删掉相应的一条(multiple=false)或多条(multiple=true)记录
     *   从效率上来看,这个集合最好采用有序集合SortedSet存储结构
     */
    private static final String QUEUE_NAME_TX = "tx_queue_transion";

    public void txTransionSend() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        //声明队列
        channel.queueDeclare(QUEUE_NAME_TX, false, false, false, null);
        try {
            String msg = "事务";
            //开启事务
            channel.txSelect();
            //发送消息
            channel.basicPublish("", QUEUE_NAME_TX, null, msg.getBytes());
            //提交事务
            channel.txCommit();
        } catch (Exception e) {
            channel.txRollback();
            e.printStackTrace();
        }

    }

    public void txTransionConsumer() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME_TX, false, false, false, null);
        //接收消息
        channel.basicConsume(QUEUE_NAME_TX, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                super.handleDelivery(consumerTag, envelope, properties, body);
                String msg = new String(body, "utf-8");
                System.out.println("msg===" + msg);
            }
        });
    }

    /**
     * confirm模式
     */
    private static final String QUEUE_NAME_CONFIRM = "confirm_queue_transion";

    public void confirmSend() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME_CONFIRM, false, false, false, null);
        //开启cnfirm事务,使用confirm的时候不能使用事务tx开头的 ,会产生异常
        channel.confirmSelect();
        String msg = "confirm hello";
        channel.basicPublish("", QUEUE_NAME_CONFIRM, null, msg.getBytes());
        /**
         * 批量发送的时候 创建多条消息发送即可,注意的是 先发送消息 再确定是否发送成功
         */
        //发完消息再确认
        if (!channel.waitForConfirms()) {
            System.out.println("消息发送成功");
        } else {
            System.out.println("消息发送失败");
        }
        channel.close();
        connection.close();
    }

    /**
     * confirm 异步模式
     *
     */
    private static final String CONFIRM_QUEUE_NAME="confirm_queue_name";
    public void confirmTransionSend() throws Exception {
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME_CONFIRM, false, false, false, null);
        //生产者调用confirmSelect(),将channel设置为confirm模式

        //未确认的消息标识
        final SortedSet<Long> confirmSet = (SortedSet<Long>) CollectionUtils.synchronizedCollection(new TreeSet<Long>());
        channel.addConfirmListener(new ConfirmListener() {
            /**
             *消息发送成功回调handleAck
             */
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                if(multiple){
                    System.out.println("发送成功");
                    confirmSet.headSet(deliveryTag+1).clear();
                }else{
                    confirmSet.remove(deliveryTag);
                }
            }

            /**
             *消息发送失败回调handleAck
             */
            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                if(multiple){
                    System.out.println("发送成功");
                    confirmSet.headSet(deliveryTag+1).clear();
                }else{
                    confirmSet.remove(deliveryTag);
                }
            }
        });

        String msg = "confirmtransion";
        while(true){
            long seqNo = channel.getNextPublishSeqNo();
            channel.basicPublish("",CONFIRM_QUEUE_NAME,null,msg.getBytes());
            confirmSet.add(seqNo);
        }
    }

看如下

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

;