【RabbitMq】rabbitMq消息确认机制

 

一、提出问题

生产者将消息发送出去后,消息是否到达RabbitMq服务器呢?默认的情况下,是不知道的

二、引入消息确认机制

两种方式:

          1.AMQP实现事务机制

          2.confirm模式

三、AMQP实现事务机制

  3.1 简单示例

txSelect():用户将当前channel设置成transaction
txCommit():提交事务
txRollback():回滚事务
package com.wj.transation.config;


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

/**
 * @创建人 wj
 * @创建时间 2018/11/2
 * @描述
 */
public class MqConfig {
    public static Connection getConnection() throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection= factory.newConnection();
        return connection;
    }

}

生产者:

package com.wj.transation;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.wj.transation.config.MqConfig;

/**
 * @创建人 wj
 * @创建时间 2018/11/5
 * @描述 用事务模式,进行消息确认机制
 */
public class TxSend {

    private static final String QUEUE_NAME = "test_queue_tx";

    public static void send() throws Exception {
        Connection connection = MqConfig.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        String msg = "hello tx!";
        try {
            // 用户将当前channel设置成transaction
            channel.txSelect();

            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());

            // 异常,使回滚
            int x=1/0;


            System.out.println("【发送端】消息:"+msg);
            //提交事务
            channel.txCommit();
        } catch (Exception e) {
            channel.txRollback();
            System.out.println("发生异常,回滚");
        }
        channel.close();
        connection.close();
    }
}

消费者

package com.wj.transation;

import com.rabbitmq.client.*;
import com.wj.transation.config.MqConfig;

import java.io.IOException;

/**
 * @创建人 wj
 * @创建时间 2018/11/5
 * @描述
 */
public class TxRecv {
    private static final String QUEUE_NAME = "test_queue_tx";

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

运行结果:

注释异常,运行结果:

  3.2 缺点

使用事务机制的话会降低RabbitMQ的性能,降低了吞吐量,增加了channel的请求数,耗时

四、Confirm模式

confirm模式分为三种:(差异在于生产者,前面二者串行

普通模式(一个)

批处理 (多个)

异步confirm模式

4.1普通模式

package com.wj.confirm.general;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.wj.confirm.util.ConnectionUtil;



/**
 * @创建人 wj
 * @创建时间 2018/11/5
 * @描述 普通模式
 */
public class Send {

    private static final String QUEUE_NAME="general_queue";

    public static void send() throws Exception {
        Connection connection=ConnectionUtil.getConnection();
        Channel channel=connection.createChannel();
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);

        //生产者调用confirmSelect 将channel设置为confirm模式
        channel.confirmSelect();

        String msg="hello confirm!";
        channel.basicPublish("",QUEUE_NAME,null,msg.getBytes());

        if (!channel.waitForConfirms()){
            System.out.println("消息发送失败!");
        }else {
            System.out.println("消息发送成功!");
        }

    }
}

4.2批量模式

package com.wj.confirm.batch;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.wj.confirm.util.ConnectionUtil;


/**
 * @创建人 wj
 * @创建时间 2018/11/5
 * @描述 批量模式
 */
public class Send {

    private static final String QUEUE_NAME = "batch_queue";

    public static void main(String[] args) throws Exception {
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //生产者调用confirmSelect 将channel设置为confirm模式
        channel.confirmSelect();

        //        批量
        for (int i = 0; i < 10; i++) {
            String msg = "hello confirm! [" + i + "]";
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
        }

        //确认
        if (!channel.waitForConfirms()) {
            System.out.println("消息发送失败!");
        } else {
            System.out.println("消息发送成功!");
        }

    }
}

4.3异步confirm模式

package com.wj.confirm.asynchronization;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.wj.confirm.util.ConnectionUtil;

import java.io.IOException;
import java.util.*;


/**
 * @创建人 wj
 * @创建时间 2018/11/5
 * @描述 批量模式
 */
public class Send {

    private static final String QUEUE_NAME = "ack_queue";

    public static void main(String[] args) throws Exception{
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        //生产者调用confirmSelect 将channel设置为confirm模式
        channel.confirmSelect();
        //        未确认的消息标识
        final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
        //        通道添加监听
        channel.addConfirmListener(new ConfirmListener() {
            //            没有问题的handleAck
            @Override
            public void handleAck(long deliverTag, boolean multiple) throws IOException {
                if (multiple) {
                    System.out.println("---handleNack----multiple");
                    confirmSet.headSet(deliverTag + 1).clear();
                } else {
                    System.out.println("false");
                    confirmSet.remove(deliverTag);
                }
            }

            @Override
            public void handleNack(long deliverTag, boolean multiple) throws IOException {
                if (multiple) {
                    System.out.println("---handleNack----multiple");
                    confirmSet.headSet(deliverTag + 1).clear();
                } else {
                    System.out.println("false");
                    confirmSet.remove(deliverTag);
                }
            }
        });


        String msg = "hello confirm——ack! ";
        while (true) {
            long seqNo = channel.getNextPublishSeqNo();
            channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
            confirmSet.add(seqNo);
        }
    }

}

1,2的顺序,2,1也可以,推荐是2,1

消费者:

package com.wj.confirm.asynchronization;

import com.rabbitmq.client.*;
import com.wj.confirm.util.ConnectionUtil;

import java.io.IOException;

/**
 * @创建人 wj
 * @创建时间 2018/11/5
 * @描述
 */
public class Recv {

    private static final String QUEUE_NAME = "ack_queue";

    public static void main(String[] args) throws Exception{
        Connection connection = ConnectionUtil.getConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);

        channel.basicConsume(QUEUE_NAME,true,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("[接收端]消息:"+new String(body,"utf-8"));
            }
        });
    }

}

gitHub:

https://github.com/InnocenceWj/rabbitmq_msg__confirm.git

猜你喜欢

转载自blog.csdn.net/heni6560/article/details/83752316