設置消息的過期時間(TTL)
TTL, Time to Live 的簡稱, 即過期時間.
兩種方法設置 TTL
-
通過隊列屬性設置. 即隊列中所有的消息都有相同的過期時間. 在 channel.queueDeclare 方法中加入 x-message-ttl 參數實現, 單位是毫秒
-
對消息本身進行單獨設置. 即每條消息的 TTL 可以不同. 在 channel.basicPublish 方法中加入 expiration 屬性, 單位是毫秒
總結: 如果兩種方法一起使用, 則以較小的那個 TTL 爲準. 消息過期後, 消費者無法再接收該消息, 就會變成死信(Dead Message). 這個特性可以實現延遲隊列功能
Java 代碼實現
給隊列設置 TTL
@Configuration
public class RabbitConfig implements ApplicationContextAware {
private ApplicationContext applicationContext;
@PostConstruct
public RabbitAdmin rabbitAdmin() {
RabbitAdmin rabbitAdmin = applicationContext.getBean("rabbitAdmin", RabbitAdmin.class);
rabbitAdmin.declareExchange(new TopicExchange("exchange.expiration"));
Map<String, Object> argMap = new HashMap<>();
argMap.put("x-message-ttl", 2000);// 2s 過期
Queue queue = new Queue("queue.expiration", true, false, false, argMap);
rabbitAdmin.declareQueue(queue);
rabbitAdmin.declareBinding(
BindingBuilder.bind(queue).to(new TopicExchange("exchange.expiration")).with("routingkey.expiration"));
return rabbitAdmin;
}
}
@Service
public class SendMQService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(String exchange, String routingKey, String msg) {
rabbitTemplate.convertAndSend(exchange, routingKey, msg);
}
}
給每一個消息單獨設置 TTL
@Configuration
public class RabbitConfig implements ApplicationContextAware {
private ApplicationContext applicationContext;
@PostConstruct
public RabbitAdmin rabbitAdmin() {
RabbitAdmin rabbitAdmin = applicationContext.getBean("rabbitAdmin", RabbitAdmin.class);
rabbitAdmin.declareExchange(new TopicExchange("exchange.expiration"));
rabbitAdmin.declareQueue(new Queue("queue.expiration"));
rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue("queue.expiration"))
.to(new TopicExchange("exchange.expiration")).with("routingkey.expiration"));
return rabbitAdmin;
}
}
@Service
public class SendMQService {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(String exchange, String routingKey, String msg, Integer expirationTime) {
rabbitTemplate.convertAndSend(exchange, routingKey, msg, message -> {
message.getMessageProperties().setExpiration(expirationTime);
return message;
});
}
}
死信隊列
DLX(Dead-Letter-Exchange), 可以稱之爲死信交換器, 也成死信信箱. 當消息在一個隊列中變成死信(dead message) 後, 會被重新發送到另外一個交換器中, 這個交換器就是 DLX. 綁定了 DLX 的隊列就是死信隊列. 說白了就是, 有兩個隊列, 一個隊列上的消息設置了過期時間, 但沒有消費者. 另一個隊列是普通隊列, 有消費者. 後者被稱爲死信隊列. 當前一個隊列消息過期後, Rabbitmq 會自動將過期消息轉發到死信隊列裏. 然後被死信隊列的消費者消費掉. 實現消息的延遲發送功能
延遲隊列
延遲隊列是爲了存放那些延遲執行的消息,待消息過期之後消費端從隊列裏拿出來執行
實現方法
通過在 channel.queueDeclare 方法中設置 x-dead-letter-exchange 參數爲這個隊列添加 DLX
Java 代碼
@Configuration
public class RabbitConfig implements ApplicationContextAware {
private ApplicationContext applicationContext;
@PostConstruct
public RabbitAdmin rabbitAdmin() {
RabbitAdmin rabbitAdmin = applicationContext.getBean("rabbitAdmin", RabbitAdmin.class);
// 死信隊列
rabbitAdmin.declareExchange(new DirectExchange("exchange.dlx"));
rabbitAdmin.declareQueue(new Queue("queue.dlx"));
rabbitAdmin.declareBinding(
BindingBuilder.bind(new Queue("queue.dlx")).to(new DirectExchange("exchange.dlx")).with("routingkey.dlx"));
// 延遲隊列
Map<String, Object> argMap = new HashMap<>(3);
argMap.put("x-message-ttl", 2000);// 2s 過期
argMap.put("x-dead-letter-exchange", "exchange.dlx");
argMap.put("x-dead-letter-routing-key", "routingkey.dlx");
rabbitAdmin.declareQueue(new Queue("queue.normal", true, false, false, argMap));
rabbitAdmin.declareExchange(new TopicExchange("exchange.normal"));
rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue("queue.normal", true, false, false, argMap))
.to(new TopicExchange("exchange.normal")).with("queue.normal"));
}
}
缺點
使用死信隊列來實現消息的延遲發送. 如果是採用第一種方式, 即每個隊列設置相同的過期時間, 可以很好的實現消息的延遲發送功能. 如果採用第二種方式, 給每個消息設置不同的過期時間, 由於隊列先入先出的特性, 如果隊列頭的消息過期時間很長, 後面的消息過期時間很短, 會導致後面的消息過期後不能及時被消費掉
簡單的做法時, 使用 rabbitmq 的延遲插件: Rabbitmq 通過延遲插件實現延遲隊列