RabbitMQ学习05--消息可靠性

消息可靠性:

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 }
原文地址:https://www.cnblogs.com/asenyang/p/15502023.html