一.ack模式
ack模式
在 spring boot 中提供了三种确认模式:
NONE - 使用rabbitmq的自动确认
AUTO - 使用rabbitmq的手动确认, springboot会自动发送确认回执 (默认)
MANUAL - 使用rabbitmq的手动确认, 且必须手动执行确认操作
默认的 AUTO 模式中, 处理消息的方法抛出异常, 则表示消息没有被正确处理, 该消息会被重新发送.
spring:
rabbitmq:
listener:
simple:
# acknowledgeMode: NONE # rabbitmq的自动确认
acknowledgeMode: AUTO # rabbitmq的手动确认, springboot会自动发送确认回执 (默认)
# acknowledgeMode: MANUAL # rabbitmq的手动确认, springboot不发送回执, 必须自己编码发送回执
手动执行确认操作
如果设置为 MANUAL 模式,必须手动执行确认操作
@RabbitListener(queues="task_queue")
public void receive1(String s, Channel c, @Header(name=AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {
System.out.println("receiver1 - 收到: "+s);
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '.') {
Thread.sleep(1000);
}
}
// 手动发送确认回执
c.basicAck(tag, false);
}
二、消费限额
工作模式中, 为了合理地分发数据, 需要将 qos 设置成 1, 每次只接收一条消息, 处理完成后才接收下一条消息.
spring boot 中是通过 prefetch 属性进行设置, 改属性的默认值是 250.
spring:
rabbitmq:
listener:
simple:
prefetch: 1 # qos=1, 默认250
三、当有多条消息存入到消息队列中时,可实现消费者高并发接收队列消息问题
/**
- testDirectRabbit 是监听对列的名称
- concurrency min-max 表示并发数,表示有多少个消费者处理队列里的消息 最小-最大数
*/
@RabbitListener(queues = “testDirectQueue”,concurrency=“5-10”)
public class DirectReceiver {
@RabbitHandler
public void process(Map testMessage){
System.out.println(Thread.currentThread().getName()+testMessage.toString());
}
}
四、设置消息过期时间TTL
主要有2种方式:
1、指定一条消息的过期时间。
2、给队列设置消息过期时间,队列中的所有消息都有同样的过期时间。
1、指定消息的过期时间(消息推送到队列后,如果指定时间内没有被消费,则会自动过期。)
@RestController
public class TTLController {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostMapping("/testTTL")
public String testTTL() {
MessageProperties messageProperties = new MessageProperties();
messageProperties.setExpiration("20000"); // 设置过期时间,单位:毫秒
byte[] msgBytes = "测试消息自动过期".getBytes();
Message message = new Message(msgBytes, messageProperties);
rabbitTemplate.convertAndSend("TTL_EXCHANGE", "TTL", message);
return "ok";
}
}
2、给队列中的所有消息设置过期时间(在队列绑定的时候添加x-message-ttl参数)
@Configuration
public class TTLQueueRabbitConfig {
@Bean
public Queue TTLQueue() {
Map<String, Object> map = new HashMap<>();
map.put("x-message-ttl", 30000); // 队列中的消息未被消费则30秒后过期
return new Queue("TTL_QUEUE", true, false, false, map);
}
@Bean
public DirectExchange TTLExchange() {
return new DirectExchange("TTL_EXCHANGE", true, false);
}
@Bean
public Binding bindingDirect() {
return BindingBuilder.bind(TTLQueue()).to(TTLExchange()).with("TTL");
}
}
五、实现延时队列(原理ttl+死信队列就可以实现)
给缓冲队列设置超时时间,而消费者并不从这个缓冲队列中拿消息,这样等消息ttl变成死信队列后就转发给了实际队列,消费者只需要绑定实际队列,从实际队列拿消息,这样就可以实现延时。
在RabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由转发死信交换机在发送到指定的队列。
x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchange
x-dead-letter-routing-key:出现dead letter之后将dead letter重新按照指定的routing-key发送
@Configuration
public class DelayQueueConfig {
public final static String DELAY_QUEUE_PER_QUEUE_TTL_NAME = "delay_queue_per_queue_ttl";
public final static String DELAY_PROCESS_QUEUE_NAME = "delay_process_queue";
public final static String DELAY_EXCHANGE_NAME = "delay_exchange";
public final static int QUEUE_EXPIRATION = 4000;
@Bean
Queue delayQueuePerQueueTTL() {
return QueueBuilder.durable(DELAY_QUEUE_PER_QUEUE_TTL_NAME)
.withArgument("x-dead-letter-exchange", DELAY_EXCHANGE_NAME) // DLX
.withArgument("x-dead-letter-routing-key", DELAY_PROCESS_QUEUE_NAME) // dead letter携带的routing key
.withArgument("x-message-ttl", QUEUE_EXPIRATION) // 设置队列的过期时间
.build();
}
@Bean
Queue delayProcessQueue() {
return QueueBuilder.durable(DELAY_PROCESS_QUEUE_NAME)
.build();
}
@Bean
DirectExchange delayExchange() {
return new DirectExchange(DELAY_EXCHANGE_NAME);
}
@Bean
Binding dlxBinding(Queue delayProcessQueue, DirectExchange delayExchange) {
return BindingBuilder.bind(delayProcessQueue)
.to(delayExchange)
.with(DELAY_PROCESS_QUEUE_NAME);
}
}