RabbitMQ(二)——消息确认机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Back_Light_F/article/details/82886064

RabbitMQ的消息确认机制(事务+confirm)

 两种方式:

       AMQP:实现了事务机制

      Confirm模式

事务机制

txSelect:用于将当前channel设置成transation模式

txCommit:用于事务提交

txRollback:回滚事务

代码实操:

生产者:

public class TxSend {
	private static String QUEUE_NAME = "test_queue_tx";

	public static void main(String[] args) throws Exception {
		
		//获取一个连接
		Connection  connection =  ConnectionUtils.getConnection();
		//从连接中获取一个通道
		Channel channel = connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);	
		String msg = "hello tx";
		try {
			channel.txSelect();
			channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
			channel.txCommit();
			
		} catch (Exception e) {
			channel.txRollback();
			System.out.println("send msg rollback");
		}	
		channel.close();
		connection.close();		
	}
}

消费者:

public class TxRecv {
	
	private static String QUEUE_NAME = "test_queue_tx";
	
	public static void main(String[] args) throws Exception {
		
		//获取一个连接
		Connection  connection =  ConnectionUtils.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, BasicProperties properties, byte[] body)
					throws IOException {
				System.out.println("recv tx msg:"+new String(body));
			}
		});
		
	}

}

测试:

出错后,回滚,消费者接收不到:

缺点:降低吞吐量

Confirm模式:

生产者端confirm模式的实现原理:

     生产者将信道设置成confirm模式,一旦信道进入confirm模式,所有在该信道上面发布的消息都会被指派一个唯一的ID(从1开始),一旦消息被投递到所匹配的队列之后,broker就会发送一个确认给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息就会将消息写入磁盘后发出,broker回传给生产者的确认消息中deliver-tag域包含了确认消息的序列号,此外broker也可以设置basic.ack的multiple域,表示到这个序列号之前的所有消息都已经得到了处理。

Confirm模式最大的好处在于异步。

开启confirm模式:channel.confirmSelect();

编程模式:

   普通模式:发一条 waitForConfirms();

  代码实操:

/*
 * 普通模式
 */
public class Send1 {
	
	private static String QUEUE_NAME = "test_queue_confirm1";
	
	public static void main(String[] args) throws Exception {
		
		//获取一个连接
		Connection  connection =  ConnectionUtils.getConnection();
		//从连接中获取一个通道
		Channel channel = connection.createChannel();
		//声明队列
		channel.queueDeclare(QUEUE_NAME, false, false, false, null);	
		
		//生产者调用confirmSelect(),将channel设置为confirm模式
		channel.confirmSelect();
		
		String msg = "hello confirm1";
		channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
		
		if(!channel.waitForConfirms()){
			System.out.println("msg send fail");
		}else{
			System.out.println("msg send succ");
		}
		channel.close();
		connection.close();
	}

}

    消费者:

public class Recv1 {
	
	private static String QUEUE_NAME = "test_queue_confirm1";
	
	public static void main(String[] args) throws Exception {
		
		//获取一个连接
		Connection  connection =  ConnectionUtils.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, BasicProperties properties, byte[] body)
					throws IOException {
				System.out.println("recv confirm msg:"+new String(body));
			}
		});
		
	}

}

测试:

消费者收到消息:

生产者收到消息回执:

批量模式:发一批 waitForConfirms

每次批量发送多条消息,消费者全部收到后,再通知生产者。这里就不贴代码了。

异步confirm模式:提供一个回调方法

       Channel对象提供的ConfirmListener()回调方法只包含deliveryTag(当前Channel发出的消息序号),我们需要自己为每一个Channel维护一个unconfirm的消息序号集合,每publish一条数据,集合中元素加1,每回调一次handleAck方法,unconfirm集合删掉相应一条(multiple=false)或多条(multiple=true)记录。

代码实操:

生产者:

public class Send3 {
	
	private static final String QUEUE_NAME = "test_queue_confirm3";
	
	public static void main(String[] args) throws Exception {
		
		Connection connection = ConnectionUtils.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() {
			//handleNack,失败的话,看业务场景如何处理
			public void handleNack(long deliveryTag, boolean multiple) throws 
                                          IOException{
				if(multiple){
					System.out.println("-----handleNack-------multiple");
					confirmSet.headSet(deliveryTag+1).clear();
				}else{
					System.out.println("---handleNac-----multiple false");
					confirmSet.remove(deliveryTag);
				}
				
			}
			//没有问题的handleAck
			public void handleAck(long deliveryTag, boolean multiple) throws IOException {
				if(multiple){
					System.out.println("-----handleAck-------multiple");
					confirmSet.headSet(deliveryTag+1).clear();
				}else{
					System.out.println("---handleAck-----multiple false");
					confirmSet.remove(deliveryTag);
				}
				
			}
		});
		
		String msg = "ssss"; 
		
		while(true){
			long seqNo  = channel.getNextPublishSeqNo();
			channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
			//向set中添加没有回执的消息的标识
			confirmSet.add(seqNo);
		}
	}
}

消费者代码和之前一样,此处就略过了。

测试:

猜你喜欢

转载自blog.csdn.net/Back_Light_F/article/details/82886064