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", "RobertoHuang@foxmail.com");
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", "RobertoHuang@foxmail.com");
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", "RobertoHuang@foxmail.com");
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上获取被系列博客源码