五、消息中间件RabbitMQ之消息的拒绝与死信交换器

消息的拒绝

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、备用交换器是在声明主交换器时发生联系,而死信交换器则声明队列时发生联系。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值