RabbitMQ的四种交换机使用xulong_87231
一、Direct exchange---直连类型交换机
直连型交换机中绑定queue时需要指定路由key,发送消息时也需要指定路由key。当消息发送到对应的exchange中,rabbitMQ会到找到路由完全匹配的key的queue中。
1、定义exchange、queue、key
/**
* 直连交换机
* 根据消息发送的key,找到交换机中对应key的queue
*/
String DIRECT_EXCHANGE = "direct_exchange";
String DIRECT_EXCHANGE_ONE = "direct_exchange_one";
String DIRECT_EXCHANGE_ONE_KEY = "direct_exchange_one_key";
String DIRECT_EXCHANGE_TWO = "direct_exchange_two";
String DIRECT_EXCHANGE_TWO_KEY = "direct_exchange_two_key";
2、绑定注册exchange、queue
//直连交换机
@Bean
public DirectExchange directExchange() {
return new DirectExchange(RabbitmqDefinition.DIRECT_EXCHANGE);
}
@Bean
public Queue directExchangeOneQueue() {
return new Queue(RabbitmqDefinition.DIRECT_EXCHANGE_ONE, true);
}
@Bean
public Binding directExchangeOneQueueBinding() {
return BindingBuilder
.bind(directExchangeOneQueue()).to(directExchange()).with(RabbitmqDefinition.DIRECT_EXCHANGE_ONE_KEY);
}
@Bean
public Queue directExchangeTwoQueue() {
return new Queue(RabbitmqDefinition.DIRECT_EXCHANGE_TWO, true);
}
@Bean
public Binding directExchangeTwoQueueBinding() {
return BindingBuilder
.bind(directExchangeTwoQueue()).to(directExchange()).with(RabbitmqDefinition.DIRECT_EXCHANGE_TWO_KEY);
}
3、定义消费者
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.DIRECT_EXCHANGE_ONE)
public void directExchangeOne(Message message, @Payload String payload, Channel channel) {
log.info("当前queue:{},接受到消息:{}", "DIRECT_EXCHANGE_ONE", payload);
}
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.DIRECT_EXCHANGE_TWO)
public void directExchangeTwo(Message message, @Payload String payload, Channel channel) {
log.info("当前queue:{},接受到消息:{}", "DIRECT_EXCHANGE_TWO", payload);
}
4、定义生产者
@Resource
private RabbitTemplate rabbitTemplate;
@Scheduled(fixedRate = 5000)
public void directExchangeMessage() {
log.info("EXCHANGE {}", "DIRECT_EXCHANGE");
Map<String, String> map = new HashMap<>();
map.put("exchange", "DIRECT_EXCHANGE");
map.put("key", "direct_exchange_one_key");
rabbitTemplate.convertAndSend(RabbitmqDefinition.DIRECT_EXCHANGE,RabbitmqDefinition.DIRECT_EXCHANGE_ONE_KEY, JSONObject.toJSONString(map));
map.put("key", "direct_exchange_two_key");
rabbitTemplate.convertAndSend(RabbitmqDefinition.DIRECT_EXCHANGE,RabbitmqDefinition.DIRECT_EXCHANGE_TWO_KEY, JSONObject.toJSONString(map));
}
5、消费者输出结果
当前queue:DIRECT_EXCHANGE_TWO,接受到消息:{"exchange":"DIRECT_EXCHANGE","key":"direct_exchange_two_key"}
当前queue:DIRECT_EXCHANGE_ONE,接受到消息:{"exchange":"DIRECT_EXCHANGE","key":"direct_exchange_one_key"}
二、Fanout Exchange---扇出类型交换机xulong_87231
扇出类型Exchange绑定queue时不需要指定路由key,当消费发送到交换机时,交换机会将消息发送到所以绑定当前 Exchange的queue。
1、定义exchange、queue、key
/**
* 扇形交换机
* 往改交换机发送的消息,绑定了当前交换机的queue都能接收到消息
*/
String FANOUT_EXCHANGE = "fanout_exchange";
String FANOUT_EXCHANGE_ONE = "fanout_exchange_one";
String FANOUT_EXCHANGE_TWO = "fanout_exchange_two";
2、绑定注册exchange、queue
// 扇形交换机
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange(RabbitmqDefinition.FANOUT_EXCHANGE);
}
@Bean
public Queue fanoutExchangeOneQueue() {
return new Queue(RabbitmqDefinition.FANOUT_EXCHANGE_ONE, true);
}
@Bean
public Binding fanoutExchangeOneQueueBinding() {
return BindingBuilder
.bind(fanoutExchangeOneQueue()).to(fanoutExchange());
}
@Bean
public Queue fanoutExchangeTwoQueue() {
return new Queue(RabbitmqDefinition.FANOUT_EXCHANGE_TWO, true);
}
@Bean
public Binding fanoutExchangeTwoQueueBinding() {
return BindingBuilder
.bind(fanoutExchangeTwoQueue()).to(fanoutExchange());
}
3、定义消费者
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.FANOUT_EXCHANGE_ONE)
public void fanoutExchangeOne(Message message, @Payload String payload, Channel channel) {
log.info("当前queue:{},接受到消息:{}", "FANOUT_EXCHANGE_ONE", payload);
}
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.FANOUT_EXCHANGE_TWO)
public void fanoutExchangeTwo(Message message, @Payload String payload, Channel channel) {
log.info("当前queue:{},接受到消息:{}", "FANOUT_EXCHANGE_TWO", payload);
}
4、定义生产者
@Resource
private RabbitTemplate rabbitTemplate;
@Scheduled(fixedRate = 10000)
public void fanoutExchangeMessage() {
log.info("EXCHANGE {}", "FANOUT_EXCHANGE");
Map<String, String> map = new HashMap<>();
map.put("exchange", "FANOUT_EXCHANGE");
rabbitTemplate.convertAndSend(RabbitmqDefinition.FANOUT_EXCHANGE,"", JSONObject.toJSONString(map));
}
5、消费者输出结果
当前queue:FANOUT_EXCHANGE_ONE,接受到消息:{"exchange":"FANOUT_EXCHANGE"}
当前queue:FANOUT_EXCHANGE_TWO,接受到消息:{"exchange":"FANOUT_EXCHANGE"}
三、Topic Exchange---主题类型交换机
Topic Exchange与Direct exchange相似,不过Direct exchange消息接受需要全匹配路由key,Topic Exchange只需要模糊匹配,与绑定exchange的路由key相关,当不需要匹配时使用匹配符合*
1、定义exchange、queue、key
/**
* 主题交换机
* 发送到交换机的消息指定规则,交换机将根据对应规则发送到对应的queue中
* 类似于直连交换机的部分匹配
*/
String TOPIC_EXCHANGE = "topic_exchange";
String TOPIC_EXCHANGE_ONE = "topic_exchange_one";
String TOPIC_EXCHANGE_TWO = "topic_exchange_two";
String TOPIC_EXCHANGE_THREE = "topic_exchange_three";
String TOPIC_EXCHANGE_ONE_KEY = "season.*.*";
String TOPIC_EXCHANGE_TWO_KEY = "*.colour.*";
String TOPIC_EXCHANGE_THREE_KEY = "*.*.action";
2、绑定注册exchange、queue
// 主题交换机
@Bean
public TopicExchange topicExchange() {
return new TopicExchange(RabbitmqDefinition.TOPIC_EXCHANGE);
}
@Bean
public Queue topicExchangeOneQueue() {
return new Queue(RabbitmqDefinition.TOPIC_EXCHANGE_ONE, true);
}
@Bean
public Queue topicExchangeTwoQueue() {
return new Queue(RabbitmqDefinition.TOPIC_EXCHANGE_TWO, true);
}
@Bean
public Queue topicExchangeThreeQueue() {
return new Queue(RabbitmqDefinition.TOPIC_EXCHANGE_THREE, true);
}
@Bean
public Binding topicExchangeOneQueueBinding() {
return BindingBuilder
.bind(topicExchangeOneQueue()).to(topicExchange()).with(RabbitmqDefinition.TOPIC_EXCHANGE_ONE_KEY);
}
@Bean
public Binding topicExchangeTwoQueueBinding() {
return BindingBuilder
.bind(topicExchangeTwoQueue()).to(topicExchange()).with(RabbitmqDefinition.TOPIC_EXCHANGE_TWO_KEY);
}
@Bean
public Binding topicExchangeThreeQueueBinding() {
return BindingBuilder
.bind(topicExchangeThreeQueue()).to(topicExchange()).with(RabbitmqDefinition.TOPIC_EXCHANGE_THREE_KEY);
}
3、定义消费者
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.TOPIC_EXCHANGE_ONE)
public void topicExchangeOne(Message message, @Payload String payload, Channel channel) {
log.info("当前queue:{},接受到消息:{}", "TOPIC_EXCHANGE_ONE", payload);
}
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.TOPIC_EXCHANGE_TWO)
public void topicExchangeTwo(Message message, @Payload String payload, Channel channel) {
log.info("当前queue:{},接受到消息:{}", "TOPIC_EXCHANGE_TWO", payload);
}
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.TOPIC_EXCHANGE_THREE)
public void topicExchangeThree(Message message, @Payload String payload, Channel channel) {
log.info("当前queue:{},接受到消息:{}", "TOPIC_EXCHANGE_THREE", payload);
}
4、定义生产者
@Scheduled(fixedRate = 10000)
public void topicExchangeMessage() {
log.info("EXCHANGE {}", "TOPIC_EXCHANGE");
Map<String, String> map = new HashMap<>();
map.put("exchange", "TOPIC_EXCHANGE");
map.put("key", "season.colour.other");
map.put("message", "春天里的树叶是绿色的");
rabbitTemplate.convertAndSend(RabbitmqDefinition.TOPIC_EXCHANGE,"season.colour.other", JSONObject.toJSONString(map));
map.put("key", "other.colour.action");
map.put("message", "一只白色的兔子在草地上奔跑");
rabbitTemplate.convertAndSend(RabbitmqDefinition.TOPIC_EXCHANGE,"other.colour.action", JSONObject.toJSONString(map));
map.put("key", "other.colour.action");
map.put("message", "冬天的动物似乎都不太愿意动起来,只有在扑食时才会追击猎物");
rabbitTemplate.convertAndSend(RabbitmqDefinition.TOPIC_EXCHANGE,"season.other.action", JSONObject.toJSONString(map));
}
5、消费者输出结果
当前queue:TOPIC_EXCHANGE_TWO,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"春天里的树叶是绿色的","key":"season.colour.other"}
当前queue:TOPIC_EXCHANGE_ONE,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"春天里的树叶是绿色的","key":"season.colour.other"}
当前queue:TOPIC_EXCHANGE_THREE,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"一只白色的兔子在草地上奔跑","key":"other.colour.action"}
当前queue:TOPIC_EXCHANGE_TWO,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"一只白色的兔子在草地上奔跑","key":"other.colour.action"}
当前queue:TOPIC_EXCHANGE_ONE,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"冬天的动物似乎都不太愿意动起来,只有在扑食时才会追击猎物","key":"other.colour.action"}
当前queue:TOPIC_EXCHANGE_THREE,接受到消息:{"exchange":"TOPIC_EXCHANGE","message":"冬天的动物似乎都不太愿意动起来,只有在扑食时才会追击猎物","key":"other.colour.action"}
四、Headers Exchanges---头部类型交换机
不处理路由键。而是根据发送的消息内容中的headers属性进行匹配。
1、定义exchange、queue、key
String HEADERS_EXCHANGE = "headers_exchange";
String HEADERS_EXCHANGE_ONE = "headers_exchange_one";
String HEADERS_EXCHANGE_TWO = "headers_exchange_two";
2、绑定注册exchange、queue
@Bean
public HeadersExchange headersExchange() {
return new HeadersExchange(RabbitmqDefinition.HEADERS_EXCHANGE);
}
@Bean
public Queue headersExchangeOneQueue() {
return new Queue(RabbitmqDefinition.HEADERS_EXCHANGE_ONE);
}
@Bean
public Queue headersExchangeTwoQueue() {
return new Queue(RabbitmqDefinition.HEADERS_EXCHANGE_TWO);
}
/**
* whereAny 只要符合其中一个请求头,queue可接受消息
* whereAll 所有请求头符合,queue可以接受消息
*/
@Bean
public Binding headersExchangeOneQueueBinding() {
Map<String, Object> map = new HashMap<>();
map.put("exchange", "HEADERS_EXCHANGE");
map.put("bindType", "whereAny");
return BindingBuilder
.bind(headersExchangeOneQueue()).to(headersExchange()).whereAny(map).match();
}
@Bean
public Binding headersExchangeTwoQueueBinding() {
Map<String, Object> map = new HashMap<>();
map.put("exchange", "HEADERS_EXCHANGE");
map.put("queue", "two");
map.put("bindType", "whereAll");
return BindingBuilder
.bind(headersExchangeTwoQueue()).to(headersExchange()).whereAll(map).match();
}
3、定义消费者
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.HEADERS_EXCHANGE_ONE)
public void headersExchangeOne(Message message, @Payload String payload, Channel channel) {
log.info("当前queue:{},接受到消息:{}", "HEADERS_EXCHANGE_ONE", payload);
}
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.HEADERS_EXCHANGE_TWO)
public void headersExchangeTwo(Message message, @Payload String payload, Channel channel) {
log.info("当前queue:{},接受到消息:{}", "HEADERS_EXCHANGE_TWO", payload);
}
4、定义生产者
@Scheduled(fixedRate = 10000)
public void headersExchangeMessage() {
log.info("EXCHANGE {}", "HEADERS_EXCHANGE");
MessageProperties messageProperties = new MessageProperties();
messageProperties.setHeader("exchange", "HEADERS_EXCHANGE");
messageProperties.setHeader("bindType", "whereAny");
Message messageOne = new Message("第一次发送数据".getBytes(), messageProperties);
rabbitTemplate.convertAndSend(RabbitmqDefinition.HEADERS_EXCHANGE,null, messageOne);
messageProperties.setHeader("queue", "two");
messageProperties.setHeader("bindType", "whereAll");
Message messageTwo = new Message("第二次发送数据".getBytes(), messageProperties);
rabbitTemplate.convertAndSend(RabbitmqDefinition.HEADERS_EXCHANGE,null, messageTwo);
}
5、消费者输出结果
当前queue:HEADERS_EXCHANGE_ONE,接受到消息:第一次发送数据
当前queue:HEADERS_EXCHANGE_TWO,接受到消息:第二次发送数据
当前queue:HEADERS_EXCHANGE_ONE,接受到消息:第二次发送数据
死信队列
死信队列介绍
- 死信队列:DLX,dead-letter-exchange
- 利用DLX,当消息在一个队列中变成死信 (dead message) 之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX
什么是死信队列
顾名思义,死信队列就是当前队列死掉了就是死信队列。当被指定的死信队列因为以下原因,则会将消息转发到对应绑定的队列中
- 消息被拒绝(basic.reject / basic.nack),并且requeue = false
- 消息TTL过期
- 队列达到最大长度
死信队列有什么作用
- 当消息处理异常,需要后续处理时可以使用死信队列
- 延迟队列,比如订单提交后30分支未支付订单取消
代码演示
1、定义exchange、queue、key
- 死信队列包括交换机及队列
- 死信队列其实就是一个正常的队列
/**
* 死信队列相关
*/
String DXL_EXCHANGE = "dxl_exchange";
String DXL_QUEUE = "dxl_queue";
String DXL_KEY = "dxl_key";
// 业务相关
String BUSINESS_EXCHANGE = "business_exchange";
String BUSINESS_QUEUE_ONE = "business_queue_one";
String BUSINESS_QUEUE_TWO = "business_queue_two";
2、绑定注册exchange、queue
/**
* 死信队列其实就是一个正常的交换机及队列
*/
@Bean
public DirectExchange dxlExchange() {
return new DirectExchange(RabbitmqDefinition.DXL_EXCHANGE);
}
@Bean
public Queue dxlQueue() {
return new Queue(RabbitmqDefinition.DXL_QUEUE, true);
}
@Bean
public Binding dxlQueueBinding() {
return BindingBuilder
.bind(dxlQueue()).to(dxlExchange()).with(RabbitmqDefinition.DXL_KEY);
}
/**
* 业务交换机及队列
* BUSINESS_QUEUE_ONE 正常业务处理,参照组
* BUSINESS_QUEUE_TWO 设置为死信的业务队列
*/
@Bean
public FanoutExchange businessExchange() {
return new FanoutExchange(RabbitmqDefinition.BUSINESS_EXCHANGE);
}
@Bean
public Queue businessQueueOneQueue() {
return new Queue(RabbitmqDefinition.BUSINESS_QUEUE_ONE, true);
}
@Bean
public Binding businessQueueOneQueueBinding() {
return BindingBuilder
.bind(businessQueueOneQueue()).to(businessExchange());
}
@Bean
public Queue businessQueueTwoQueue() {
Map<String, Object> map = new HashMap<>();
// 声明当前死信队列绑定的死信交换机,可以是任何类型的交换机
map.put("x-dead-letter-exchange", RabbitmqDefinition.DXL_EXCHANGE);
// 声明当前队列的死信路由键
map.put("x-dead-letter-routing-key", RabbitmqDefinition.DXL_KEY);
// 设置队列消息的超时时间,单位毫秒,超过时间进入死信队列,也可以在发送消息处设置超时时间,都设置时,以最小超时时间为准
map.put("x-message-ttl",3000);
// 生命队列的最大长度,超过长度的消息进入死信队列
map.put("x-max-length", 10);
return new Queue(RabbitmqDefinition.BUSINESS_QUEUE_TWO, true, false, false, map);
}
@Bean
public Binding businessQueueTwoQueueBinding() {
return BindingBuilder
.bind(businessQueueTwoQueue()).to(businessExchange());
}
3、定义消费者
- 做延迟队列业务操作时,可以不设置消费者
- 如下消费者为模拟业务处理失败,转发到指定的死信队列
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.DXL_QUEUE)
public void dxlQueue(Message message, @Payload String payload, Channel channel) {
log.info("当前queue为死信队列转发队列:{},接受到消息:{}", "DXL_QUEUE", payload);
}
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.BUSINESS_QUEUE_ONE)
public void businessQueueOne(Message message, @Payload String payload, Channel channel) {
log.info("当前queue为正常参照业务:{},接受到消息:{}", "BUSINESS_QUEUE_ONE", payload);
}
@RabbitHandler
@RabbitListener(queues = RabbitmqDefinition.BUSINESS_QUEUE_TWO)
public void businessQueueTwo(Message message, @Payload String payload, Channel channel) throws IOException {
log.info("当前queue为业务死信队列:{},接受到消息:{}", "BUSINESS_QUEUE_TWO", payload);
try {
String str = null;
str.equals("测试报错");
}catch (Exception e) {
// 第三个参数设置为false,则当前mq消息消费失败不会重新发送到当前mq
channel.basicNack(message.getMessageProperties().getDeliveryTag(), true, false);
}
}
4、定义生产者
@Scheduled(fixedRate = 10000)
public void dxlExchangeMessage() {
Map<String, String> map = new HashMap<>();
map.put("exchange", "BUSINESS_EXCHANGE");
map.put("content", "测试业务");
rabbitTemplate.convertAndSend(RabbitmqDefinition.BUSINESS_EXCHANGE,"", JSONObject.toJSONString(map));
// 在发送时设置消息超时时间
// rabbitTemplate.convertAndSend(RabbitmqDefinition.BUSINESS_EXCHANGE,"", JSONObject.toJSONString(map), message -> {
// // 发送消息时设置消息的超时时间
// message.getMessageProperties().setExpiration("5000");
// return message;
// });
}
5、消费者输出结果
- 下方两个输出结果分别为处理业务异常及不配置消费者的延迟队列
2022-04-01 11:10:16.245 INFO 11200 --- [ntContainer#6-1] c.example.demo.rabbitmq.MessageListener : 当前queue为正常参照业务:BUSINESS_QUEUE_ONE,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}
2022-04-01 11:10:16.245 INFO 11200 --- [ntContainer#9-1] c.example.demo.rabbitmq.MessageListener : 当前queue为业务死信队列:BUSINESS_QUEUE_TWO,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}
2022-04-01 11:10:16.247 INFO 11200 --- [tContainer#10-1] c.example.demo.rabbitmq.MessageListener : 当前queue为死信队列转发队列:DXL_QUEUE,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}
2022-04-01 11:11:15.215 INFO 21016 --- [ntContainer#7-1] c.example.demo.rabbitmq.MessageListener : 当前queue为正常参照业务:BUSINESS_QUEUE_ONE,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}
2022-04-01 11:11:18.218 INFO 21016 --- [ntContainer#4-1] c.example.demo.rabbitmq.MessageListener : 当前queue为死信队列转发队列:DXL_QUEUE,接受到消息:{"exchange":"BUSINESS_EXCHANGE","content":"测试业务"}