消息的拒绝
Reject拒绝
生成者
package com.kevin.task.reject;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 描述:Reject消息拒绝方式----生产者<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-9-26 15:37<br/>
* 版本:1.0
*/
public class RejectProduct {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.101");
connectionFactory.setPort(5672);
connectionFactory.setUsername("kevin");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("myhost");
String exchangeName = "rejectExchange";
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT);
for(int i=0;i<10;i++){
String message = "Order" + (i+1);
channel.basicPublish(exchangeName,"rejectRoutingKey",null,message.getBytes());
System.out.println("下单[rejectRoutingKey]" + message);
}
channel.close();
connection.close();
}
}
正常消费者A
package com.kevin.task.reject;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 描述:正常消费者A消费消息<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-9-26 15:48<br/>
* 版本:1.0
*/
public class RejectNormalConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.101");
connectionFactory.setPort(5672);
connectionFactory.setUsername("kevin");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("myhost");
String exhangeName = "rejectExchange";
String rountingKey = "rejectRoutingKey";
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exhangeName, BuiltinExchangeType.DIRECT);
String queueName = "rejectQueue";
channel.queueDeclare(queueName,false,false,false,null);
channel.queueBind(queueName,exhangeName,rountingKey,null);
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者A["+envelope.getRoutingKey()+"]" + new String(body,"UTF-8"));
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(queueName,false,consumer);
}
}
正常消费者B
package com.kevin.task.reject;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 描述:正常消费者B消费消息<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-9-26 15:48<br/>
* 版本:1.0
*/
public class RejectNormalConsumerB {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.101");
connectionFactory.setPort(5672);
connectionFactory.setUsername("kevin");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("myhost");
String exhangeName = "rejectExchange";
String rountingKey = "rejectRoutingKey";
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exhangeName, BuiltinExchangeType.DIRECT);
String queueName = "rejectQueue";
channel.queueDeclare(queueName,false,false,false,null);
channel.queueBind(queueName,exhangeName,rountingKey,null);
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者B["+envelope.getRoutingKey()+"]" + new String(body,"UTF-8"));
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(queueName,false,consumer);
}
}
拒绝消费者
package com.kevin.task.reject;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 描述:Reject消息拒绝消费者<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-9-26 15:47<br/>
* 版本:1.0
*/
public class RejectConsumer1 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.101");
connectionFactory.setPort(5672);
connectionFactory.setUsername("kevin");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("myhost");
String exhangeName = "rejectExchange";
String rountingKey = "rejectRoutingKey";
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exhangeName, BuiltinExchangeType.DIRECT);
String queueName = "rejectQueue";
channel.queueDeclare(queueName,false,false,false,null);
channel.queueBind(queueName,exhangeName,rountingKey,null);
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消息拒绝["+envelope.getRoutingKey()+"]" + new String(body,"UTF-8"));
try {
throw new RuntimeException("拒绝消息" + new String(body,"UTF-8"));
}catch (RuntimeException e){
channel.basicReject(envelope.getDeliveryTag(),true);
}
}
};
channel.basicConsume(queueName,false,consumer);
}
}
运行结果
=============================================
生产者共生产了以下消息
下单[rejectRoutingKey]Order1
下单[rejectRoutingKey]Order2
下单[rejectRoutingKey]Order3
下单[rejectRoutingKey]Order4
下单[rejectRoutingKey]Order5
下单[rejectRoutingKey]Order6
下单[rejectRoutingKey]Order7
下单[rejectRoutingKey]Order8
下单[rejectRoutingKey]Order9
下单[rejectRoutingKey]Order10
=============================================
消费者A消费了一下几条消息
消费者A[rejectRoutingKey]Order2
消费者A[rejectRoutingKey]Order5
消费者A[rejectRoutingKey]Order8
消费者A[rejectRoutingKey]Order1
消费者A[rejectRoutingKey]Order10
=============================================
消费者B消费了一下几条消息
消费者B[rejectRoutingKey]Order3
消费者B[rejectRoutingKey]Order6
消费者B[rejectRoutingKey]Order9
消费者B[rejectRoutingKey]Order4
消费者B[rejectRoutingKey]Order7
=============================================
拒绝消费者拒绝了一下几条消息
消息拒绝[rejectRoutingKey]Order1
消息拒绝[rejectRoutingKey]Order4
消息拒绝[rejectRoutingKey]Order7
消息拒绝[rejectRoutingKey]Order10
消息拒绝[rejectRoutingKey]Order7
=============================================
从上面的结果可以看出三个消费者使用轮询方式消费消息,Reject设置requeue=true后消息会重新被消费。
NACK拒绝
使用NACK拒绝方式只要修改拒绝那块就行了上面使用了channel.basicReject()方式拒绝,只要把这个修改成channel.basicNack()方式拒绝即可,在这里多一个参数,这个参数就是设置是否批量拒绝。
修改Reject拒绝消费者后的代码
package com.kevin.task.reject;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 描述:Nack消息拒绝消费者<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-9-26 15:47<br/>
* 版本:1.0
*/
public class NackConsumer2 {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.101");
connectionFactory.setPort(5672);
connectionFactory.setUsername("kevin");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("myhost");
String exhangeName = "rejectExchange";
String rountingKey = "rejectRoutingKey";
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exhangeName, BuiltinExchangeType.DIRECT);
String queueName = "rejectQueue";
channel.queueDeclare(queueName,false,false,false,null);
channel.queueBind(queueName,exhangeName,rountingKey,null);
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消息拒绝["+envelope.getRoutingKey()+"]" + new String(body,"UTF-8"));
try {
throw new RuntimeException("拒绝消息" + new String(body,"UTF-8"));
}catch (RuntimeException e){
//multiple是否批量拒绝,requeue是否重新投递
channel.basicNack(envelope.getDeliveryTag(),false,true);
}
}
};
channel.basicConsume(queueName,false,consumer);
}
}
这里运行结果与Reject的运行结果效果是一样的。
requeue消息重新投递设置
消息被拒绝之后消息是否可以重新投递,如果设置true的话那么可以重新投递,如果设置false,那么消息被拒绝之后消息直接丢了。
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消息拒绝["+envelope.getRoutingKey()+"]" + new String(body,"UTF-8"));
//这里第二个参数就是requeue,如果设置true就会重新投递了
channel.basicReject(envelope.getDeliveryTag(),true);
}
};
Reject与NACK区别
Reject消息拒绝只能单条拒绝,而NACK能够提供批量拒绝方式。
死信交换器DLX
RabbitMQ对AMQP规范的一个扩展。被投递消息被拒绝后的一个可选行为,往往用在对问题消息的诊断上。
消息变成死信一般是以下几种情况:
• 消息被拒绝,并且设置 requeue 参数为 false
• 消息过期
• 队列达到最大长度(丢弃最先进入的消息)
死信交换器仍然只是一个普通的交换器,创建时并没有特别要求和操作。在创建队列的时候,声明该交换器将用作保存被拒绝的消息即可,相关的参数是x-dead-letter-exchange。
生产者
package com.kevin.task.dlx;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 描述:死信交换器DLX----生产者<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-9-26 15:37<br/>
* 版本:1.0
*/
public class DLXProduct {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.101");
connectionFactory.setPort(5672);
connectionFactory.setUsername("kevin");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("myhost");
String exchangeName = "dlxExchange";
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT);
for(int i=0;i<10;i++){
String message = "Order" + (i+1);
channel.basicPublish(exchangeName,"dlxRoutingKey",null,message.getBytes());
System.out.println("下单[rejectRoutingKey]" + message);
}
channel.close();
connection.close();
}
}
拒绝消费者
package com.kevin.task.dlx;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
/**
* 描述:死信交换器DLX----普通消费者<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-9-26 15:48<br/>
* 版本:1.0
*/
public class DLXNormalConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.101");
connectionFactory.setPort(5672);
connectionFactory.setUsername("kevin");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("myhost");
String exhangeName = "dlxExchange";
String rountingKey = "dlxRoutingKey";
Map<String,Object> params = new HashMap<>();
params.put("x-dead-letter-exchange","dlxExchange_dlx");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exhangeName, BuiltinExchangeType.DIRECT);
String queueName = "dlxNormalQueue";
//这里绑定死信交换器
channel.queueDeclare(queueName,false,true,false,params);
channel.queueBind(queueName,exhangeName,rountingKey,null);
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消息拒绝["+envelope.getRoutingKey()+"]" + new String(body,"UTF-8"));
//设置不重新发送
channel.basicReject(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(queueName,false,consumer);
}
}
在拒绝消费者,设置队列的时候需要传入map参数,需要设置x-dead-letter-exchange属性,这个属性就是设置死信交换器名称。
死信交换器消费者
package com.kevin.task.dlx;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
/**
* 描述:死信交换器DLX----死信消费者<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-9-26 15:48<br/>
* 版本:1.0
*/
public class DLXConsumer {
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.101");
connectionFactory.setPort(5672);
connectionFactory.setUsername("kevin");
connectionFactory.setPassword("123456");
connectionFactory.setVirtualHost("myhost");
String exhangeName = "dlxExchange_dlx";
String rountingKey = "dlxRoutingKey";
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(exhangeName, BuiltinExchangeType.DIRECT);
String queueName = "dlxQueue";
channel.queueDeclare(queueName,false,false,false,null);
channel.queueBind(queueName,exhangeName,rountingKey,null);
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("死信消费["+envelope.getRoutingKey()+"]" + new String(body,"UTF-8"));
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
channel.basicConsume(queueName,false,consumer);
}
}
运行结果
=============================================
生产者下单数据信息
下单[rejectRoutingKey]Order1
下单[rejectRoutingKey]Order2
下单[rejectRoutingKey]Order3
下单[rejectRoutingKey]Order4
下单[rejectRoutingKey]Order5
下单[rejectRoutingKey]Order6
下单[rejectRoutingKey]Order7
下单[rejectRoutingKey]Order8
下单[rejectRoutingKey]Order9
下单[rejectRoutingKey]Order10
=============================================
拒绝消费者结果
消息拒绝[dlxRoutingKey]Order1
消息拒绝[dlxRoutingKey]Order2
消息拒绝[dlxRoutingKey]Order3
消息拒绝[dlxRoutingKey]Order4
消息拒绝[dlxRoutingKey]Order5
消息拒绝[dlxRoutingKey]Order6
消息拒绝[dlxRoutingKey]Order7
消息拒绝[dlxRoutingKey]Order8
消息拒绝[dlxRoutingKey]Order9
消息拒绝[dlxRoutingKey]Order10
=============================================
死信消费者消费情况
死信消费[dlxRoutingKey]Order1
死信消费[dlxRoutingKey]Order2
死信消费[dlxRoutingKey]Order3
死信消费[dlxRoutingKey]Order4
死信消费[dlxRoutingKey]Order5
死信消费[dlxRoutingKey]Order6
死信消费[dlxRoutingKey]Order7
死信消费[dlxRoutingKey]Order8
死信消费[dlxRoutingKey]Order9
死信消费[dlxRoutingKey]Order10
=============================================
死信交换器与备用交换器区别
1、备用交换器是主交换器无法路由消息,那么消息将被路由到这个新的备用交换器,而死信交换器则是接收过期或者被拒绝的消息。
2、备用交换器是在声明主交换器时发生联系,而死信交换器则声明队列时发生联系。