消息可靠性:
1.如果消息已经到达了rabbitmq,但mq宕机了,消息并不会丢失。rabbitmq有持久化机制。
2.消费者在消费消息时,没有执行完就宕机了,消息并不会从mq清除,消费者可以使用手动ack机制。
3.生产者发送消息时由于网络问题,消息没有发送到rabbitmq,可以采用以下两种机制:事务操作,confirm操作。
1、RabbitMQ事务(不推荐):
事务可以保证消息100%传递,可以通过事务的回滚,后面定时再次发送当前消息。
事务机制的缺点:效率低。加了事务要比正常速度低100倍。
2、Confirm确认机制:
Confirm机制是确保生产者将消息发送给了RabbitMQ的exchange。
2.1 普通Confirm方式:
1 package com.yas.confirm; 2 3 import com.rabbitmq.client.Channel; 4 import com.rabbitmq.client.Connection; 5 import com.yas.config.RabbitMQClient; 6 import org.junit.Test; 7 8 public class Publisher { 9 @Test 10 public void publish() throws Exception { 11 //1.获取连接对象 12 Connection connection = RabbitMQClient.getConnection(); 13 //2.创建Channel 14 Channel channel = connection.createChannel(); 15 //3.发布消息到exchange,同时指定路由规则 16 17 //3.1 开启Confirm 18 channel.confirmSelect(); 19 20 String msg = "Hello World"; 21 //参数1:指定exchange,使用空字符串,表示默认exchange。 22 //参数2:指定路由规则,使用具体的队列名称。 23 //参数3:指定传递的消息所携带的properties。 24 //参数4:指定发布的具体消息,字节数组类型byte[] 25 channel.basicPublish("", "HelloWorld", null, msg.getBytes()); 26 //注意:exchange是不会将消息持久化到本地的,Queue有持久化的功能。 27 28 //判断消息发送是否成功 29 if (channel.waitForConfirms()) { 30 System.out.println("消息发送成功"); 31 }else{ 32 System.out.println("消息发送失败"); 33 } 34 35 System.out.println("生产者发布消息成功"); 36 //4.释放资源 37 channel.close(); 38 connection.close(); 39 } 40 }
2.2 批量Confirm方式:
1 package com.yas.confirm; 2 3 import com.rabbitmq.client.Channel; 4 import com.rabbitmq.client.Connection; 5 import com.yas.config.RabbitMQClient; 6 import org.junit.Test; 7 8 public class Publisher2 { 9 @Test 10 public void publish() throws Exception { 11 //1.获取连接对象 12 Connection connection = RabbitMQClient.getConnection(); 13 //2.创建Channel 14 Channel channel = connection.createChannel(); 15 //3.发布消息到exchange,同时指定路由规则 16 17 //3.1 开启Confirm 18 channel.confirmSelect(); 19 20 String msg = "Hello World"; 21 //参数1:指定exchange,使用空字符串,表示默认exchange。 22 //参数2:指定路由规则,使用具体的队列名称。 23 //参数3:指定传递的消息所携带的properties。 24 //参数4:指定发布的具体消息,字节数组类型byte[] 25 for (int i = 0; i < 1000; i++) { 26 msg = msg + i; 27 channel.basicPublish("", "HelloWorld", null, msg.getBytes()); 28 } 29 //注意:exchange是不会将消息持久化到本地的,Queue有持久化的功能。 30 31 //判断消息发送是否成功 32 //当发送的消息有一个失败时,就会全部失败 33 channel.waitForConfirmsOrDie(); 34 35 System.out.println("生产者发布消息成功"); 36 //4.释放资源 37 channel.close(); 38 connection.close(); 39 } 40 }
2.3 异步Confirm方式(推荐):
1 package com.yas.confirm; 2 3 import com.rabbitmq.client.Channel; 4 import com.rabbitmq.client.ConfirmListener; 5 import com.rabbitmq.client.Connection; 6 import com.yas.config.RabbitMQClient; 7 import org.junit.Test; 8 9 import java.io.IOException; 10 11 public class Publisher3 { 12 @Test 13 public void publish() throws Exception { 14 //1.获取连接对象 15 Connection connection = RabbitMQClient.getConnection(); 16 //2.创建Channel 17 Channel channel = connection.createChannel(); 18 //3.发布消息到exchange,同时指定路由规则 19 20 //3.1 开启Confirm 21 channel.confirmSelect(); 22 23 24 //参数1:指定exchange,使用空字符串,表示默认exchange。 25 //参数2:指定路由规则,使用具体的队列名称。 26 //参数3:指定传递的消息所携带的properties。 27 //参数4:指定发布的具体消息,字节数组类型byte[] 28 for (int i = 0; i < 1000; i++) { 29 String msg = "Hello World"; 30 msg = msg + i; 31 channel.basicPublish("", "HelloWorld", null, msg.getBytes()); 32 } 33 //注意:exchange是不会将消息持久化到本地的,Queue有持久化的功能。 34 35 //判断消息发送是否成功 36 //异步发送 37 channel.addConfirmListener(new ConfirmListener() { 38 @Override 39 public void handleAck(long deliveryTag, boolean multiple) throws IOException { 40 System.out.println("消息发送成功,标识:" + deliveryTag + ",是否是批量:" + multiple); 41 } 42 43 @Override 44 public void handleNack(long deliveryTag, boolean multiple) throws IOException { 45 System.out.println("消息发送失败,标识:" + deliveryTag + ",是否是批量:" + multiple); 46 } 47 }); 48 49 System.out.println("生产者发布消息成功"); 50 51 System.in.read(); 52 //4.释放资源 53 channel.close(); 54 connection.close(); 55 } 56 }
3、Return机制:
Return机制是确保在RabbitMQ内部,exchange将消息发送给queue。
而且exchange是不能持久化消息的,queue才可以持久化,因此消息发送到queue才能保证不丢失。
采用Return机制,监听消息是否从exchange发送到了queue。
生产者代码:
1 package com.yas.myreturn; 2 3 import com.rabbitmq.client.*; 4 import com.yas.config.RabbitMQClient; 5 import org.junit.Test; 6 7 import java.io.IOException; 8 9 public class Publisher { 10 @Test 11 public void publish() throws Exception { 12 //1.获取连接对象 13 Connection connection = RabbitMQClient.getConnection(); 14 //2.创建Channel 15 Channel channel = connection.createChannel(); 16 17 //3.1 开启Confirm 18 channel.confirmSelect(); 19 20 //判断消息发送是否成功 21 //异步发送 22 channel.addConfirmListener(new ConfirmListener() { 23 @Override 24 public void handleAck(long deliveryTag, boolean multiple) throws IOException { 25 System.out.println("消息发送成功,标识:" + deliveryTag + ",是否是批量:" + multiple); 26 } 27 28 @Override 29 public void handleNack(long deliveryTag, boolean multiple) throws IOException { 30 System.out.println("消息发送失败,标识:" + deliveryTag + ",是否是批量:" + multiple); 31 } 32 }); 33 34 //3.发布消息到exchange,同时指定路由规则 35 36 //开启Return机制 37 channel.addReturnListener(new ReturnListener() { 38 //当消息没有送到queue时,才会执行 39 @Override 40 public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException { 41 System.out.println(new String(body, "UTF-8") + "没有送到Queue中"); 42 } 43 }); 44 45 //参数1:指定exchange,使用空字符串,表示默认exchange。 46 //参数2:指定路由规则,使用具体的队列名称。 47 //参数3:指定传递的消息所携带的properties。 48 //参数4:指定发布的具体消息,字节数组类型byte[] 49 // channel.basicPublish("", "HelloWorld", null, msg.getBytes()); 50 51 //使用带return机制的发布,mandatory:true 52 for (int i = 0; i < 1000; i++) { 53 String msg = "Hello World"; 54 msg = msg + i; 55 channel.basicPublish("", "HelloWorld",true, null, msg.getBytes()); 56 } 57 58 //注意:exchange是不会将消息持久化到本地的,Queue有持久化的功能。 59 60 System.out.println("生产者发布消息成功"); 61 System.in.read(); 62 //4.释放资源 63 channel.close(); 64 connection.close(); 65 } 66 }
消费者代码:
1 package com.yas.myreturn; 2 3 import com.rabbitmq.client.*; 4 import com.yas.config.RabbitMQClient; 5 import org.junit.Test; 6 7 import java.io.IOException; 8 9 public class Consumer { 10 @Test 11 public void consume() throws Exception{ 12 //1.获取连接对象 13 Connection connection = RabbitMQClient.getConnection(); 14 //2.创建channel 15 Channel channel = connection.createChannel(); 16 //3.生命队列-Hello World 17 //参数1:queue,队列名称 18 //参数2:durable,当前队列是否需要持久化 19 //参数3:exclusive,是否排外 20 // 影响1:当connection.close()时,当前队列会被自动删除 21 // 影响2:当前队列只能被一个消费者消费 22 //参数4:autoDelete,如果这个队列没有消费者消费,队列自动删除 23 //参数5:arguments,指定当前队列的其他信息 24 channel.queueDeclare("HelloWorld",true,false,false,null); 25 //4.开启监听Queue 26 DefaultConsumer consumer = new DefaultConsumer(channel){ 27 @Override 28 public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { 29 //super.handleDelivery(consumerTag, envelope, properties, body); 30 System.out.println("接受到消息:" + new String(body,"UTF-8")); 31 } 32 }; 33 //参数1:queue,指定消费哪个队列 34 //参数2:deliverCallback,指定是否自动ACK,(true表示,接受到消息后,会立即通知RabbitMQ) 35 //参数3:consumer,指定消费回调 36 channel.basicConsume("HelloWorld",true,consumer); 37 System.out.println("消费者开始监听队列"); 38 System.in.read(); 39 //5/释放资源 40 channel.close(); 41 connection.close(); 42 } 43 }