(七) RabbitMQ实战教程(面向Java开发人员)之RabbitMQ常用属性详解

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

RabbitMQ常用属性详解

本系列博客源码GIT地址:https://github.com/RobertoHuang/RGP-RABBITMQ.git

Alternate Exchange

Alternate Exchange简称AE,当消息不能被正确路由时,如果交换机设置了AE则消息会被投递到AE中,如果存在AE链则会按此继续投递,直到消息被正确路由或AE链结束消息被丢弃。通常建议AE的交换机类型为Fanout防止出现路由失败,如果一个交换机指定了AE那么意为着该交换机和AE链都无法被正确路由时才会触发消息返回

RabbitMQ Java Client

1.创建连接工具类

public class ChannelUtils {
    public static Channel getChannelInstance(String connectionDescription) {
        try {
            ConnectionFactory connectionFactory = getConnectionFactory();
            Connection connection = connectionFactory.newConnection(connectionDescription);
            return connection.createChannel();
        } catch (Exception e) {
            throw new RuntimeException("获取Channel连接失败");
        }
    }

    private static ConnectionFactory getConnectionFactory() {
        ConnectionFactory connectionFactory = new ConnectionFactory();

        connectionFactory.setHost("192.168.56.128");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("roberto");
        connectionFactory.setPassword("roberto");

        connectionFactory.setAutomaticRecoveryEnabled(true);
        connectionFactory.setNetworkRecoveryInterval(10000);

        Map<String, Object> connectionFactoryPropertiesMap = new HashMap();
        connectionFactoryPropertiesMap.put("principal", "RobertoHuang");
        connectionFactoryPropertiesMap.put("description", "RGP订单系统V2.0");
        connectionFactoryPropertiesMap.put("emailAddress", "[email protected]");
        connectionFactory.setClientProperties(connectionFactoryPropertiesMap);

        return connectionFactory;
    }
}

2.创建消息生产者 并将roberto.order交换机的AE指向roberto.order.failure交换机 并发送一条不能被正确路由的消息

public class MessageProducer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = ChannelUtils.getChannelInstance("RGP订单系统消息生产者");

        // 声明AE 类型为Fanout
        channel.exchangeDeclare("roberto.order.failure", BuiltinExchangeType.FANOUT, true, false, false, new HashMap<>());
        // 为roberto.order设置AE
        Map<String, Object> exchangeProperties = new HashMap<>();
        exchangeProperties.put("alternate-exchange", "roberto.order.failure");
        channel.exchangeDeclare("roberto.order", BuiltinExchangeType.DIRECT, true, false, false, exchangeProperties);

        // 发送一条不能正确路由的消息
        AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder().deliveryMode(2).contentType("UTF-8").build();
        channel.basicPublish("roberto.order", "addXXX", false, basicProperties, "订单信息".getBytes());
    }
}

3.创建消息消费者 将roberto.order交换机AE指向roberto.order.failure交换机,并将roberto.order.add队列绑定到roberto.order交换机上routing key为add,将roberto.order.add.failure队列绑定到roberto.order.failure交换机上,同时监听这两个队列

public class MessageConsumer {
    public static void main(String[] args) throws IOException, TimeoutException {
        Channel channel = ChannelUtils.getChannelInstance("RGP订单系统消息消费者");

        AMQP.Queue.DeclareOk declareOk = channel.queueDeclare("roberto.order.add", true, false, false, new HashMap<>());
        // 声明AE 类型为Fanout
        channel.exchangeDeclare("roberto.order.failure", BuiltinExchangeType.FANOUT, true, false, false, new HashMap<>());
        // 为roberto.order设置AE
        Map<String, Object> exchangeProperties = new HashMap<>();
        exchangeProperties.put("alternate-exchange", "roberto.order.failure");
        channel.exchangeDeclare("roberto.order", BuiltinExchangeType.DIRECT, true, false, false, exchangeProperties);
        channel.queueBind(declareOk.getQueue(), "roberto.order", "add", new HashMap<>());

        // 将roberto.order.add.failure队列绑定到roberto.order.failure交换机上 无需指定routing key
        AMQP.Queue.DeclareOk declareOk2 = channel.queueDeclare("roberto.order.add.failure", true, false, false, new HashMap<>());
        channel.queueBind(declareOk2.getQueue(), "roberto.order.failure", "", new HashMap<>());

        // 消费roberto.order.add队列
        channel.basicConsume(declareOk.getQueue(), false, "RGP订单系统ADD处理逻辑消费者", new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    System.out.println("----------roberto.order.add----------");
                    System.out.println(new String(body, "UTF-8"));
                    channel.basicAck(envelope.getDeliveryTag(), false);
                } catch (Exception e) {
                    channel.basicNack(envelope.getDeliveryTag(), false, true);
                }
            }
        });

        // 消费roberto.order.add.failure队列
        channel.basicConsume(declareOk2.getQueue(), false, "RGP订单系统ADD FAILURE处理逻辑消费者", new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    System.out.println("----------roberto.order.add.failure----------");
                    System.out.println(new String(body, "UTF-8"));
                    channel.basicAck(envelope.getDeliveryTag(), false);
                } catch (Exception e) {
                    channel.basicNack(envelope.getDeliveryTag(), false, true);
                }
            }
        });
    }
}

4.依次启动消息消费者和消息生产者,控制台打印

----------roberto.order.add.failure----------
订单消息信息

上诉例子中可以看出当交换机不能正确路由消息时,会将消息投递给AE进行处理

Spring AMQP配置方式

1.创建消息生产者配置类

@Configuration
public class SpringAMQPProducerConfig {
    @Bean
    public ConnectionFactory connectionFactory() {
        com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();

        connectionFactory.setHost("192.168.56.128");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("roberto");
        connectionFactory.setPassword("roberto");

        connectionFactory.setAutomaticRecoveryEnabled(true);
        connectionFactory.setNetworkRecoveryInterval(10000);

        Map<String, Object> connectionFactoryPropertiesMap = new HashMap();
        connectionFactoryPropertiesMap.put("principal", "RobertoHuang");
        connectionFactoryPropertiesMap.put("description", "RGP订单系统V2.0");
        connectionFactoryPropertiesMap.put("emailAddress", "[email protected]");
        connectionFactory.setClientProperties(connectionFactoryPropertiesMap);

        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(connectionFactory);
        return cachingConnectionFactory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        return new RabbitTemplate(connectionFactory);
    }
}

2.创建生产者启动类 将roberto.order交换机的AE指向roberto.order.failure交换机 并发送一条不能被正确路由的消息

@ComponentScan(basePackages = "roberto.growth.process.rabbitmq.attribute.alternate.exchange.spring.amqp.producer")
public class ProducerApplication {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(roberto.growth.process.rabbitmq.dependable.consumer.spring.amqp.producer.ProducerApplication.class);

        RabbitAdmin rabbitAdmin = context.getBean(RabbitAdmin.class);
        RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);

        // 声明AE 类型为Fanout
        rabbitAdmin.declareExchange(new FanoutExchange("roberto.order.failure", true, false, new HashMap<>()));

        // 为roberto.order设置AE
        Map<String, Object> exchangeProperties = new HashMap<>();
        exchangeProperties.put("alternate-exchange", "roberto.order.failure");
        rabbitAdmin.declareExchange(new DirectExchange("roberto.order", true, false, exchangeProperties));

        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
        messageProperties.setContentType("UTF-8");
        Message message = new Message("订单信息".getBytes(), messageProperties);
        rabbitTemplate.send("roberto.order", "addXXX", message, new CorrelationData("201210704116"));
    }
}

3.创建消费者配置类 将roberto.order交换机AE指向roberto.order.failure交换机,并将roberto.order.add队列绑定到roberto.order交换机上routing key为add,将roberto.order.add.failure队列绑定到roberto.order.failure交换机上,同时监听这两个队列

@Configuration
public class SpringAMQPConsumerConfig {
    @Bean
    public ConnectionFactory connectionFactory() {
        com.rabbitmq.client.ConnectionFactory connectionFactory = new com.rabbitmq.client.ConnectionFactory();

        connectionFactory.setHost("192.168.56.128");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("roberto");
        connectionFactory.setPassword("roberto");

        connectionFactory.setAutomaticRecoveryEnabled(true);
        connectionFactory.setNetworkRecoveryInterval(10000);

        Map<String, Object> connectionFactoryPropertiesMap = new HashMap();
        connectionFactoryPropertiesMap.put("principal", "RobertoHuang");
        connectionFactoryPropertiesMap.put("description", "RGP订单系统V2.0");
        connectionFactoryPropertiesMap.put("emailAddress", "[email protected]");
        connectionFactory.setClientProperties(connectionFactoryPropertiesMap);

        CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(connectionFactory);
        return cachingConnectionFactory;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean
    public List<Queue> queueList() {
        Queue queue = new Queue("roberto.order.add", true, false, false, new HashMap<>());
        Queue queue2 = new Queue("roberto.order.add.failure", true, false, false, new HashMap<>());
        return Arrays.asList(queue, queue2);
    }

    @Bean
    public List<Exchange> exchangeList() {
        // 声明AE 类型为Fanout
        FanoutExchange fanoutExchange = new FanoutExchange("roberto.order.failure", true, false, new HashMap<>());
        Map<String, Object> exchangeProperties = new HashMap<>();
        exchangeProperties.put("alternate-exchange", "roberto.order.failure");
        DirectExchange directExchange = new DirectExchange("roberto.order", true, false, exchangeProperties);
        return Arrays.asList(fanoutExchange, directExchange);
    }

    @Bean
    public List<Binding> bindingList() {
        Binding binding = BindingBuilder.bind(new Queue("roberto.order.add")).to(new DirectExchange("roberto.order")).with("add");
        Binding binding2 = BindingBuilder.bind(new Queue("roberto.order.add.failure")).to(new DirectExchange("roberto.order.failure")).with("");
        return Arrays.asList(binding, binding2);
    }

    @Bean
    public MessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer messageListenerContainer = new SimpleMessageListenerContainer();
        messageListenerContainer.setConnectionFactory(connectionFactory);
        messageListenerContainer.setQueueNames("roberto.order.add");

        messageListenerContainer.setConcurrentConsumers(5);
        messageListenerContainer.setMaxConcurrentConsumers(10);

        Map<String, Object> argumentMap = new HashMap();
        messageListenerContainer.setConsumerArguments(argumentMap);
        messageListenerContainer.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String s) {
                return "RGP订单系统ADD处理逻辑消费者";
            }
        });

        messageListenerContainer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                try {
                    System.out.println("----------roberto.order.add----------");
                    System.out.println(new String(message.getBody(), "UTF-8"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        return messageListenerContainer;
    }

    @Bean
    public MessageListenerContainer messageListenerContainer2(ConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer messageListenerContainer = new SimpleMessageListenerContainer();
        messageListenerContainer.setConnectionFactory(connectionFactory);
        messageListenerContainer.setQueueNames("roberto.order.add.failure");

        messageListenerContainer.setConcurrentConsumers(5);
        messageListenerContainer.setMaxConcurrentConsumers(10);

        Map<String, Object> argumentMap = new HashMap();
        messageListenerContainer.setConsumerArguments(argumentMap);
        messageListenerContainer.setConsumerTagStrategy(new ConsumerTagStrategy() {
            @Override
            public String createConsumerTag(String s) {
                return "RGP订单系统ADD FAILURE处理逻辑消费者";
            }
        });

        messageListenerContainer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                try {
                    System.out.println("----------roberto.order.add.failure----------");
                    System.out.println(new String(message.getBody(), "UTF-8"));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        return messageListenerContainer;
    }
}

4.创建消费者启动类

@ComponentScan(basePackages = "roberto.growth.process.rabbitmq.attribute.alternate.exchange.spring.amqp.consumer")
public class ConsumerApplication {
    public static void main(String[] args) {
        new AnnotationConfigApplicationContext(ConsumerApplication.class);
    }
}

5.依次启动消息消费者和生产者 控制台输出如下

----------roberto.order.add.failure----------
订单信息

如上代码使用Spring AMQP实现的效果和RabbitMQ Java Client中实现的效果一致

消息存活时间 TTL

TTL(Time To Live)指的是消息的存活时间,当消息超过过期时间后,消息即成为死信。RabbitMQ可以分别为队列和消息本身设置消息过期时间,注意当队列过期时间和消息过期时间都存在时,取两者中较短的时间

RabbitMQ Java Client

// 设置消息将在三秒后过期
new AMQP.BasicProperties().builder().expiration("3000")

// 设置队列中的消息 将在5秒后过期
Map<String, Object> queueProperties = new HashMap<>();
queueProperties.put("x-message-ttl", 5000);
Queue addFailureQueue = new Queue("roberto.order.add.failure", true, false, false, queueProperties);

Spring AMQP配置方式

// 设置消息将在三秒后过期
messageProperties.setExpiration("3000");

// 设置队列中的消息 将在5秒后过期
Map<String, Object> queueProperties = new HashMap<>();
queueProperties.put("x-message-ttl", 5000);
Queue addFailureQueue = new Queue("roberto.order.add.failure", true, false, false, queueProperties);

由于该配置较为简单此处不贴出完整代码,如有需要可从Git上获取本系列博客源码

消息数量限制 Length Limit

我们可以为队列设置x-max-length来指定队列中可存放的最大消息数量,或者设置max-length-bytes来指定队列中存放消息内容的最大字节长度,当超过这个长度后部分消息将成为死信

RabbitMQ Java Client

// 设置队列可存放最大消息数量为2条
Map<String, Object> queueProperties = new HashMap<>();
queueProperties.put("x-max-length", 2);
AMQP.Queue.DeclareOk declareOk = channel.queueDeclare("roberto.order.add", true, false, false, queueProperties);

// 设置队列可存放消息内容的最大字节长度为20
Map<String, Object> queueProperties = new HashMap<>();
queueProperties.put("x-max-length-bytes", 20);
AMQP.Queue.DeclareOk declareOk = channel.queueDeclare("roberto.order.add", true, false, false, queueProperties);

Spring AMQP配置方式

// 设置队列可存放最大消息数量为2条
Map<String, Object> queueProperties = new HashMap<>();
queueProperties.put("x-max-length", 2);
AMQP.Queue.DeclareOk declareOk = channel.queueDeclare("roberto.order.add", true, false, false, queueProperties);

// 设置队列可存放消息内容的最大字节长度为20
Map<String, Object> queueProperties2 = new HashMap<>();
queueProperties2.put("x-max-length-bytes", 20);
AMQP.Queue.DeclareOk declareOk2 = channel.queueDeclare("roberto.order.add.failure", true, false, false, queueProperties2);

由于该配置较为简单此处不贴出完整代码,如有需要可从Git上获取被系列博客源码

死信队列 Dead Letter Exchange

如果队列中设置了Dead Letter Exchange属性,当消息变成死信后它能重写被投递到另一个交换机。消息变成死信一般有如下几种情况

1.消息过期而被删除
2.消息数量超过队列最大限制而被删除
3.消息总大小超过队列最大限制而被删除
4.消息被拒绝(channel.basicReject()/channel.basicNack()),且requeue为false

同时也可以指定一个可选的x-dead-letter-routing-key表示默认的routing-key,如果没有指定则使用消息的routing-key

RabbitMQ Java Client

Map<String, Object> queueProperties = new HashMap<>();
queueProperties.put("x-dead-letter-exchange","roberto.order.failure");
AMQP.Queue.DeclareOk declareOk = channel.queueDeclare("roberto.order.add", true, false, false, queueProperties);

Spring AMQP配置方式

Map<String, Object> queueProperties = new HashMap<>();
queueProperties.put("x-dead-letter-exchange","roberto.order.failure");
Queue queue = new Queue("roberto.order.add", true, false, false, queueProperties);

由于该配置较为简单此处不贴出完整代码,如有需要可从Git上获取被系列博客源码

优先级队列 Priority Queue

RabbitMQ提供了优先级队列的功能

1.创建优先级队列需增加x-max-priority参数(指定一个数字 数值越大则优先级越高)
2.发送消息的时候需要设置priority属性,最好不要超过上面指定的x-max-priority,如果超过了x-max-priority则为x-max-priority,需要注意如果生产者发送消息较慢,消费者消费速度较快则可能不会严格的按照优先级队列进行消费

RabbitMQ Java Client

// 创建队列时设置队列最大优先级
Map<String, Object> queueProperties = new HashMap<>();
queueProperties.put("x-max-priority", 10);
AMQP.Queue.DeclareOk declareOk = channel.queueDeclare("roberto.order.add", true, false, false, queueProperties);

// 发送消息时使用priority()方法指定消息优先级
AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder().deliveryMode(2).contentType("UTF-8").priority(i).build();

Spring AMQP配置方式

// 创建队列时设置队列最大优先级
Map<String, Object> queueProperties = new HashMap<>();
queueProperties.put("x-max-priority", 10);
new Queue("roberto.order.add", true, false, false, queueProperties);


// 发送消息时使用setPriority()方法指定消息优先级
messageProperties.setPriority(i);

由于该配置较为简单此处不贴出完整代码,如有需要可从Git上获取被系列博客源码

猜你喜欢

转载自blog.csdn.net/RobertoHuang/article/details/79615316
今日推荐