定义:一个消息最多被一个消费者消费,多个消费者对一条消息是竞争关系。
应用场景:对于任务过重或任务较多情况使用工作队列可以提高任务处理的速度。
定义:一个消息可以被多个消费者消费。
应用场景:发布订阅、广播
关键代码
//声明交换机
String exchangeName = "exchange_fanout";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true, false, false, null);
//声明队列
String queueName1 = "fanout1";
String queueName2 = "fanout2";
channel.queueDeclare(queueName1, true, false, false, null);
channel.queueDeclare(queueName2, true, false, false, null);
//绑定交换机与队列
channel.queueBind(queueName1, exchangeName, "");
channel.queueBind(queueName2, exchangeName, "");
//发送消息
channel.basicPublish(exchangeName, "", false, null, "hello rabbitmq".getBytes());
定义:队列与交换机的绑定,不能是任意绑定了,而是要指定一个RoutingKey(路由key)。消息的发送方在向Exchange发送消息时,也必须指定消息的 RoutingKey。Exchange 不再把消息交给每一个绑定的队列,而是根据消息的Routing Key进行判断,只有队列的Routingkey 与消息的Routing key完全—致,才会接收到消息。
图解:p->生产者;X->交换机;C->消费者。
应用场景:定制化消费消息
关键代码
//声明交换机
String exchangeName = "exchange_direct";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.DIRECT, true, false, false, null);
//声明队列
String queueName1 = "direct1";
String queueName2 = "direct2";
channel.queueDeclare(queueName1, true, false, false, null);
channel.queueDeclare(queueName2, true, false, false, null);
//绑定交换机与队列
channel.queueBind(queueName1, exchangeName, "error");
channel.queueBind(queueName2, exchangeName, "info");
channel.queueBind(queueName2, exchangeName, "error");
channel.queueBind(queueName2, exchangeName, "warn");
//发送消息
channel.basicPublish(exchangeName, "info", false, null, "hello rabbitmq".getBytes());
定义:使用通配符匹配
//声明交换机
String exchangeName = "exchange_topic";
channel.exchangeDeclare(exchangeName, BuiltinExchangeType.TOPIC, true, false, false, null);
//声明队列
String queueName1 = "topic1";
String queueName2 = "topic2";
channel.queueDeclare(queueName1, true, false, false, null);
channel.queueDeclare(queueName2, true, false, false, null);
//绑定交换机与队列
channel.queueBind(queueName1, exchangeName, "*.error");
channel.queueBind(queueName1, exchangeName, "order.*");
channel.queueBind(queueName2, exchangeName, "*.*");
//发送消息
channel.basicPublish(exchangeName, "order.error", false, null, "hello rabbitmq".getBytes());
由于异常情况,无法被消费的消息,延迟消费。
1、TTL
消息过期成为死信
configuration
@Configuration
public class DeadLetterConfig {
public static final String NORMAL_EXCHANGE = "normal_exchange";//普通交换机
public static final String DEAD_EXCHANGE = "dead_exchange";//死信交换机
public static final String NORMAL_QUEUE = "normal_queue";//普通队列
public static final String DEAD_QUEUE = "dead_queue";//死信队列
public static final String NORMAL_ROUTING = "normal_routing";//普通routingKey
public static final String DEAD_ROUTING = "dead_routing";//死信routingKey
@Bean
public Exchange normalExchange() {//普通交换机
return ExchangeBuilder.directExchange(NORMAL_EXCHANGE).durable(true).build();
}
@Bean
public Exchange deadExchange() {//死信交换机
return ExchangeBuilder.directExchange(DEAD_EXCHANGE).durable(true).build();
}
@Bean
public Queue normalQueue() {//普通队列
return QueueBuilder.durable(NORMAL_QUEUE)
.deadLetterExchange(DEAD_EXCHANGE)//死信交换机
.deadLetterRoutingKey(DEAD_ROUTING)//死信routingKey
.build();
}
@Bean
public Queue deadQueue() {//死信队列
return QueueBuilder.durable(DEAD_QUEUE).build();
}
@Bean
public Binding bindingNormalQueueExchange(@Qualifier("normalExchange") Exchange normalExchange,
@Qualifier("normalQueue") Queue normalQueue) {
return BindingBuilder
.bind(normalQueue)
.to(normalExchange)
.with(NORMAL_ROUTING)
.noargs();
}
@Bean
public Binding bindingDeadQueueExchange(@Qualifier("deadExchange") Exchange deadExchange,
@Qualifier("deadQueue") Queue deadQueue) {
return BindingBuilder
.bind(deadQueue)
.to(deadExchange)
.with(DEAD_ROUTING)
.noargs();
}
}
生产者
@RestController
public class MessageController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/message/send")
public String send(String info) {
rabbitTemplate.convertAndSend(
DeadLetterConfig.NORMAL_EXCHANGE,//普通交换机
DeadLetterConfig.NORMAL_ROUTING,//普通routingKey
info,//消息
mp -> {
//20s未被消费送到死信队列
mp.getMessageProperties().setExpiration("20000");
return mp;
});
return "send success";
}
}
消费者
@Component
public class MessageListener {
@RabbitListener(queues = "dead_queue")
public void deadQueue(Message message){//死信队列监听器
System.out.println("dead message "+new String(message.getBody()));
}
@RabbitListener(queues = "normal_queue")
public void normalQueue(Message message){普通队列监听器
System.out.println("normal message "+new String(message.getBody()));
}
}
2、队列已满
超出队列容量的消息成为死信
configuration
@Configuration
public class DeadLetterConfig {
public static final String NORMAL_EXCHANGE = "normal_exchange";//普通交换机
public static final String DEAD_EXCHANGE = "dead_exchange";//死信交换机
public static final String NORMAL_QUEUE = "normal_queue";//普通队列
public static final String DEAD_QUEUE = "dead_queue";//死信队列
public static final String NORMAL_ROUTING = "normal_routing";//普通routingKey
public static final String DEAD_ROUTING = "dead_routing";//死信routingKey
@Bean
public Exchange normalExchange() {
return ExchangeBuilder.directExchange(NORMAL_EXCHANGE).durable(true).build();
}
@Bean
public Exchange deadExchange() {
return ExchangeBuilder.directExchange(DEAD_EXCHANGE).durable(true).build();
}
@Bean
public Queue normalQueue() {
return QueueBuilder.durable(NORMAL_QUEUE)
.deadLetterExchange(DEAD_EXCHANGE)//死信交换机
.deadLetterRoutingKey(DEAD_ROUTING)//死信RoutingKey
.maxLength(6)//队列最大长度为6
.build();
}
@Bean
public Queue deadQueue() {
return QueueBuilder.durable(DEAD_QUEUE).build();
}
@Bean
public Binding bindingNormalQueueExchange(@Qualifier("normalExchange") Exchange normalExchange,
@Qualifier("normalQueue") Queue normalQueue) {
return BindingBuilder
.bind(normalQueue)
.to(normalExchange)
.with(NORMAL_ROUTING)
.noargs();
}
@Bean
public Binding bindingDeadQueueExchange(@Qualifier("deadExchange") Exchange deadExchange,
@Qualifier("deadQueue") Queue deadQueue) {
return BindingBuilder
.bind(deadQueue)
.to(deadExchange)
.with(DEAD_ROUTING)
.noargs();
}
}
生产者
@RestController
public class MessageController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/message/sendMaxLength")
public String sendMaxLength(String info) {
for (int i = 1; i <= 10; i++) {//生产10条消息
rabbitTemplate.convertAndSend(
DeadLetterConfig.NORMAL_EXCHANGE,
DeadLetterConfig.NORMAL_ROUTING,
info + i
);
}
return "success";
}
}
消费者
与TTL一致,记得需要删除mq中的queue
3、消息被拒
消息被拒成为死信
configuration
@Configuration
public class DeadLetterConfig {
public static final String NORMAL_EXCHANGE = "normal_exchange";//普通交换机
public static final String DEAD_EXCHANGE = "dead_exchange";//死信交换机
public static final String NORMAL_QUEUE = "normal_queue";//普通队列
public static final String DEAD_QUEUE = "dead_queue";//死信队列
public static final String NORMAL_ROUTING = "normal_routing";//普通routingKey
public static final String DEAD_ROUTING = "dead_routing";//死信routingKey
@Bean
public Exchange normalExchange() {
return ExchangeBuilder.directExchange(NORMAL_EXCHANGE).durable(true).build();
}
@Bean
public Exchange deadExchange() {
return ExchangeBuilder.directExchange(DEAD_EXCHANGE).durable(true).build();
}
@Bean
public Queue normalQueue() {
return QueueBuilder.durable(NORMAL_QUEUE)
.deadLetterExchange(DEAD_EXCHANGE)//死信交换机
.deadLetterRoutingKey(DEAD_ROUTING)//死信RoutingKey
.build();
}
@Bean
public Queue deadQueue() {
return QueueBuilder.durable(DEAD_QUEUE).build();
}
@Bean
public Binding bindingNormalQueueExchange(@Qualifier("normalExchange") Exchange normalExchange,
@Qualifier("normalQueue") Queue normalQueue) {
return BindingBuilder
.bind(normalQueue)
.to(normalExchange)
.with(NORMAL_ROUTING)
.noargs();
}
@Bean
public Binding bindingDeadQueueExchange(@Qualifier("deadExchange") Exchange deadExchange,
@Qualifier("deadQueue") Queue deadQueue) {
return BindingBuilder
.bind(deadQueue)
.to(deadExchange)
.with(DEAD_ROUTING)
.noargs();
}
}
生产者
@RestController
public class MessageController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/message/sendRejected")
public String sendRejected(String info) {
for (int i = 1; i <= 10; i++) {
rabbitTemplate.convertAndSend(
DeadLetterConfig.NORMAL_EXCHANGE,
DeadLetterConfig.NORMAL_ROUTING,
info + i
);
}
return "success";
}
}
消费者
@Component
public class MessageListener {
@RabbitListener(queues = "boot_queue")
public void bootQueue(Message message) {
System.out.println(message.getBody());
}
@RabbitListener(queues = "dead_queue")
public void deadQueue(Message message) {
System.out.println("dead message " + new String(message.getBody()));
}
@RabbitListener(queues = "normal_queue")
public void normalQueue(Message message, Channel channel) throws IOException {
if ("oxx5".equals(new String(message.getBody()).intern())) {
//消息被拒,false代表成为死信
channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
} else {
//自动确认
channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
}
}
}
相当于消息TTL过期
创建两个队列QA和QB,两者队列TTL分别设置为10s和40s,然后在创建一个交换机X和死信交换机Y,它们的类型都是direct,创建一个死信队列QD。
configuration
@Configuration
public class TTLDeadLetterConfig {
public static final String NORMAL_EXCHANGE = "normal_exchange";//普通交换机
public static final String DEAD_EXCHANGE = "dead_exchange";//死信交换机
public static final String NORMAL_QUEUE_A = "normal_queue_a";//普通队列A
public static final String NORMAL_QUEUE_B = "normal_queue_b";//普通队列B
public static final String DEAD_QUEUE = "dead_queue";//死信队列
public static final String NORMAL_ROUTING_KEY_A = "normal_routing_key_a";//routingKeyA
public static final String NORMAL_ROUTING_KEY_B = "normal_routing_key_b";//routingKeyB
public static final String DEAD_ROUTING_KEY = "dead_routing_key";//死信routingKey
//声明交换机
@Bean
public Exchange normalExchange(){//普通交换机
return ExchangeBuilder.directExchange(NORMAL_EXCHANGE).durable(true).build();
}
@Bean
public Exchange deadExchange(){//死信交换机
return ExchangeBuilder.directExchange(DEAD_EXCHANGE).durable(true).build();
}
//声明队列
@Bean
public Queue normalQueueA(){//普通队列A
return QueueBuilder.durable(NORMAL_QUEUE_A)
.deadLetterExchange(DEAD_EXCHANGE)//死信交换机
.deadLetterRoutingKey(DEAD_ROUTING_KEY)//死信routingKey
.ttl(10000)//过期时间10s
.build();
}
@Bean
public Queue normalQueueB(){//普通队列B
return QueueBuilder.durable(NORMAL_QUEUE_B)
.deadLetterExchange(DEAD_EXCHANGE)//死信交换机
.deadLetterRoutingKey(DEAD_ROUTING_KEY)//死信routingKey
.ttl(40000)//过期时间40s
.build();
}
@Bean
public Queue deadQueue(){//死信队列
return QueueBuilder.durable(DEAD_QUEUE).build();
}
//队列与交换机绑定
@Bean
public Binding bindingExchangeNormalA(
@Qualifier("normalExchange") Exchange normalExchange,
@Qualifier("normalQueueA") Queue normalQueueA){
return BindingBuilder.bind(normalQueueA).to(normalExchange).with(NORMAL_ROUTING_KEY_A).noargs();
}
@Bean
public Binding bindingExchangeNormalB(
@Qualifier("normalExchange") Exchange normalExchange,
@Qualifier("normalQueueB") Queue normalQueueB){
return BindingBuilder.bind(normalQueueB).to(normalExchange).with(NORMAL_ROUTING_KEY_B).noargs();
}
@Bean
public Binding bindingExchangeDead(
@Qualifier("deadExchange") Exchange deadExchange,
@Qualifier("deadQueue") Queue deadQueue){
return BindingBuilder.bind(deadQueue).to(deadExchange).with(DEAD_ROUTING_KEY).noargs();
}
}
生产者
@RestController
@RequestMapping("/ttl")
@Slf4j
public class TTLController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/send/{message}")
public String send(@PathVariable("message") String message) {
log.info("send message - {}",message);
//发送消息,10s后处理
rabbitTemplate.convertAndSend(
TTLDeadLetterConfig.NORMAL_EXCHANGE,//交换机
TTLDeadLetterConfig.NORMAL_ROUTING_KEY_A,
message
);
//发送消息,40后处理
rabbitTemplate.convertAndSend(
TTLDeadLetterConfig.NORMAL_EXCHANGE,//交换机
TTLDeadLetterConfig.NORMAL_ROUTING_KEY_B,
message
);
return "success";
}
}
消费者
@Component
@Slf4j
public class TTLListener {
@RabbitListener(queues = "dead_queue")
public void ttl(Message message) {
log.info("receive message - {}",new String(message.getBody()));
}
}
上述案例有个缺点,每新增一个ttl
,需要新增一个普通队列,维护比较困难。
ttl
,在生产端动态设置过期时间configuration
@Controller
public class DelayConfig {
public static final String DELAY_EXCHANGE = "delay_exchange";//延迟交换机
public static final String DELAY_QUEUE = "delay_queue";//延迟队列
public static final String DELAY_ROUTING_KEY = "delay_routing_key";//延迟队列
@Bean
public Exchange delayExchange() {
Map<String,Object> args = new HashMap<>();
args.put("x-delayed-type","direct");
return new CustomExchange(
DELAY_EXCHANGE,
"x-delayed-message",
true,
false,
args);
}
@Bean
public Queue delayQueue() {
return QueueBuilder.durable(DELAY_QUEUE).build();
}
@Bean
public Binding bindingExchangeQueue(
@Qualifier("delayQueue") Queue queue,
@Qualifier("delayExchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(DELAY_ROUTING_KEY).noargs();
}
}
生产者
@RestController
@RequestMapping("/ttl")
@Slf4j
public class TTLController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/send/delay/{delay}/{message}")
public String send2(@PathVariable("delay") Integer delay, @PathVariable("message") String message) {
log.info("send delay - {}, message - {}", delay,message);
rabbitTemplate.convertAndSend(
DelayConfig.DELAY_EXCHANGE,
DelayConfig.DELAY_ROUTING_KEY,
message,
mp -> {
mp.getMessageProperties().setDelay(delay);//设置延迟时间
return mp;
});
return "success";
}
}
消费者
@Component
@Slf4j
public class TTLListener {
@RabbitListener(queues = "delay_queue")
public void delay(Message message) {
log.info("delay receive message - {}",new String(message.getBody()));
}
}
当备份交换机和消息回退同时存在时,以备份交换机为主
备份交换机类型为 fanout
application.yml
spring:
rabbitmq:
publisher-confirm-type: correlated #开启发布确认模式
publisher-returns: true #消息回退
configuration
@Configuration
public class ConfirmConfig {
public static final String CONFIRM_EXCHANGE = "confirm_exchange";//确认交换机
public static final String CONFIRM_QUEUE = "confirm_queue";//确认队列
public static final String CONFIRM_ROUTING_KEY = "confirm_routing_key";//确认routingKey
public static final String ALTERNATE_EXCHANGE = "alternate_exchange";//备份交换机
public static final String ALTERNATE_QUEUE = "alternate_queue";//备份队列(报警队列)
public static final String ALTERNATE_ROUTING_KEY = "alternate_routing_key";//备份routingKey
@Bean
public Exchange confirmExchange() {
return ExchangeBuilder.
directExchange(CONFIRM_EXCHANGE).
durable(true).
alternate(ALTERNATE_EXCHANGE).//备份交换机
build();
}
@Bean
public Exchange alternateExchange(){
return ExchangeBuilder.fanoutExchange(ALTERNATE_EXCHANGE).durable(true).build();
}
@Bean
public Queue confirmQueue() {
return QueueBuilder.durable(CONFIRM_QUEUE).build();
}
@Bean
public Queue alternateQueue() {
return QueueBuilder.durable(ALTERNATE_QUEUE).build();
}
@Bean
public Binding bindingExchangeQueue(
@Qualifier("confirmQueue") Queue queue,
@Qualifier("confirmExchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(CONFIRM_ROUTING_KEY).noargs();
}
@Bean
public Binding bindingExchangeWarnQueue(
@Qualifier("alternateQueue") Queue queue,
@Qualifier("alternateExchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(ALTERNATE_ROUTING_KEY).noargs();
}
}
生产者
@Slf4j
@RestController
@RequestMapping("/confirm")
public class ConfirmController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/send/{id}/{message}")
public String send(@PathVariable("id") String id, @PathVariable("message") String message) {
log.info("confirm message - {}", message);
rabbitTemplate.convertAndSend(
ConfirmConfig.CONFIRM_EXCHANGE+"1",
ConfirmConfig.CONFIRM_ROUTING_KEY,
message,
new CorrelationData(id)
);
return "success";
}
}
消费者
@Component
@Slf4j
public class ConfirmListener {
@RabbitListener(queues = "confirm_queue")
public void confirm(Message message) {//确认队列
log.info("confirm message - {}", message);
}
@RabbitListener(queues = "alternate_queue")
public void warn(Message message) {//备份队列
log.info("warn message - {}", message);
}
}
回调
public class ConfirmCallback implements
RabbitTemplate.ConfirmCallback,
RabbitTemplate.ReturnsCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init() {
rabbitTemplate.setConfirmCallback(this);
rabbitTemplate.setReturnsCallback(this);
}
@Override
public void confirm(
CorrelationData correlationData, //消息
boolean ack, //是否确认
String cause//产生原因
) {//确认消息
String id = correlationData.getId();
if (ack) {//正常接收消息
log.info("id - {}", id);
} else {//交换机异常
log.info("no adapter exchange,cause - {}", cause);
}
}
@Override
public void returnedMessage(ReturnedMessage rm) {//回退消息
log.info("exchange - {}",rm.getExchange());//交换机
log.info("replyText - {}",rm.getReplyText());//回退原因
log.info("message - {}",rm.getMessage());//回退消息
}
}
提前消费优先级高的消息
configuration
@Configuration
public class PriorityConfig {
public static final String PRIORITY_EXCHANGE = "priority_exchange";//交换机
public static final String PRIORITY_QUEUE = "priority_queue";//队列
public static final String PRIORITY_ROUTING_KEY = "priority_routing_key";//routingKey
@Bean
public Exchange priorityExchange() {
return ExchangeBuilder.directExchange(PRIORITY_EXCHANGE).durable(true).build();
}
@Bean
public Queue priorityQueue() {
return QueueBuilder
.durable(PRIORITY_QUEUE)
.maxPriority(10)//最大优先级
.build();
}
@Bean
public Binding bindingExchangePriorityQueue(
@Qualifier("priorityQueue") Queue queue,
@Qualifier("priorityExchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(PRIORITY_ROUTING_KEY).noargs();
}
}
生产者
@Slf4j
@RestController
@RequestMapping("/priority")
public class PriorityController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/send/{message}")
public String send( @PathVariable("message") String message) {
log.info("priority message - {}", message);
for (int i = 1; i <= 10; i++) {
if(i == 5){
rabbitTemplate.convertAndSend(
PriorityConfig.PRIORITY_EXCHANGE,
PriorityConfig.PRIORITY_ROUTING_KEY,
message + i,
mp -> {
mp.getMessageProperties().setPriority(5);//设置优先级
return mp;
}
);
}
else {
rabbitTemplate.convertAndSend(
PriorityConfig.PRIORITY_EXCHANGE,
PriorityConfig.PRIORITY_ROUTING_KEY,
message + i,
mp -> {
mp.getMessageProperties().setPriority(4);//设置优先级
return mp;
}
);
}
}
return "success";
}
}
消费者
@Component
@Slf4j
public class PriorityListener {
@RabbitListener(queues = "priority_queue")
public void priority(Message message){
log.info("priority message - {}",new String(message.getBody()));
}
}
configuration
@Bean
public Queue lazyQueue() {
return QueueBuilder
.durable(LAZY_QUEUE)
.lazy()
.build();
}
其他代码大致一样