DLX+TTL 存在時序問題
由於隊列先入先出的特性. 通過死信隊列(DLX)和給每條消息設置過期時間(TTL)來實現延遲隊列, 會存在時序問題. 即排在隊列頭的消息過期使時間如果設置的比較長, 會導致隊列後面過期時間比較短的消息, 過期了遲遲不被消費掉. 可以通過給 Rabbitmq 安裝延遲插件來實現延遲隊列功能
安裝延遲插件
下載地址
rabbitmq-delayed-message-exchange 插件可到這裏下載: RabbitMQ 延遲插件
也可以到github上下載 : RabbitMQ Delayed Message Plugin
(注意插件版本, 這個插件適應的版本時 3.5.8 及其以後的版本)
安裝
登錄 Linux 服務器, 將插件複製到這個路徑下: /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.15/plugins/
然後執行以下指令:
# 開啓插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
# 重啓 rabbitmq
/sbin/service rabbitmq-server restart
Java 代碼實現
@Configuration
public class RabbitConfig implements ApplicationContextAware {
private ApplicationContext applicationContext;
@PostConstruct
public RabbitAdmin rabbitAdmin() {
RabbitAdmin rabbitAdmin = applicationContext.getBean("rabbitAdmin", RabbitAdmin.class);
TopicExchange exchange = new TopicExchange("exchange.delay");
// 交換器設置延遲屬性
exchange.setDelayed(true);
rabbitAdmin.declareQueue(new Queue("queue.delay"));
rabbitAdmin.declareExchange(exchange);
rabbitAdmin.declareBinding(BindingBuilder.bind(new Queue("queue.delay")).to(exchange).with("rountingkey.delay");
return rabbitAdmin;
}
}
// 消息發送器
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;
});
}
}
// 消息監聽器, 交換器 delayed = "true"
@Component
@RabbitListener(containerFactory = "listenerContainerFactory",
bindings = @QueueBinding(value = @Queue(value = "queue.delay"),
exchange = @Exchange(value = "exchange.delay", type = ExchangeTypes.TOPIC, delayed = "true"),
key = "rountingkey.delay"))
@Slf4j
public class MsgListener {
@RabbitHandler
public void msgHandler(String msg) {
log.info("接收到的延遲消息 [{}]",msg)
}
}