方法一:通过插件实现(好处:实现了,不阻塞的延迟队列)
-
先安装rabbitMQ延迟插件 ---> rabbitmq-delayed-message-exchange (高版本的rabbitMQ安装完插件之后无需重启)
当出现添加交换机有如图所示的选项时,说明安装插件成功 -
定义常量
/** * 访客延迟队列 */ String VISITOR_DELAY_QUEUE ="yxb.visitor.delay.queue"; /** * 访客延迟交换机 */ String VISITOR_DELAY_EXCHANGE = "yxb.visitor.delay.exchange"; /** * 访客延迟消息路由key */ String VISITOR_DELAY_ROUTE_KEY = "yxb.receive.visitor.delay.routeKey";
-
创建延迟队列
/** * 访客延迟队列 * @return */ @Bean Queue visitorDelayQueue() { return new Queue(MqConstant.VISITOR_DELAY_QUEUE); }
-
创建延迟交换机
@Bean public CustomExchange visitorDelayExchange(){ HashMap<String, Object> args = new HashMap<>(); args.put("x-delayed-type","direct"); return new CustomExchange(MqConstant.VISITOR_DELAY_EXCHANGE,"x-delayed-message",true,false,args); }
-
绑定延迟队列和交换机
/** * 延迟队列绑定交换机 * @param visitorDelayQueue 延迟队列 * @param visitorDelayExchange 交换机 * @return Binding */ @Bean public Binding bindVisitorDelayExchange(@Qualifier("visitorDelayQueue") Queue visitorDelayQueue, @Qualifier("visitorDelayExchange") CustomExchange visitorDelayExchange){ return BindingBuilder.bind(visitorDelayQueue).to(visitorDelayExchange).with(MqConstant.VISITOR_DELAY_ROUTE_KEY).noargs(); }
-
发送延迟消息
rabbitTemplate.convertAndSend(mqMessage.getExchange(),mqMessage.getRoutingKey(),mqMessage, message -> { // 设置延迟毫秒值 message.getMessageProperties().setDelay(delayTimes.intValue()); return message; }, new CorrelationData(mqMessage.getId()));
-
监听延迟队列
@Component @AllArgsConstructor @Slf4j public class VisitorEventDrive { private final VisitorOrdersService visitorOrdersService; @RabbitHandler @RabbitListener(queues = MqConstant.VISITOR_DELAY_QUEUE) public void receiveEvent(Message message, Channel channel, MqMessage mqMessage){ try { //doSomething } catch (Exception e) { e.toString(); } } }
方法二:通过死信实现延迟队列(不推荐,会存在阻塞的情况)
比如:先向队列中发送一个60秒到期的消息,再往队列中发送一个30秒到期的消息.预想的是队列先执行30秒的消息,再执行60秒的消息.但是实际情况是60秒的消息会阻塞队列,导致先执行了60秒的消息,再执行30秒的消息.(延迟30秒的要求没有实现)
- 创建死信队列和转发队列
/** * 访客二维码死信队列 * @return */ @Bean Queue visitorTtlQueue() { return QueueBuilder .durable(MqConstant.VISITOR_TTL_QUEUE) // 配置到期后转发的交换 .withArgument("x-dead-letter-exchange", MqConstant.VISITOR_DIRECT_EXCHANGE) // 配置到期后转发的路由键 .withArgument("x-dead-letter-routing-key", MqConstant.TTL_CHANGE_ROUTE_KEY) .build(); } /** * 访客二维码死信消息转发队列 * @return 队列 */ @Bean public Queue visitorTtlChangeQueue(){ return new Queue(MqConstant.VISITOR_TTL_CHANGE_QUEUE); }
- 创建直连交换机和延时交换机
/** * 访客直连交换机 * @return 交换机 */ @Bean public DirectExchange visitorDirectExchange(){ return new DirectExchange(MqConstant.VISITOR_DIRECT_EXCHANGE); } /** * 延时消息交换机配置 * @return DirectExchange */ @Bean DirectExchange messageTtlDirect() { return (DirectExchange) ExchangeBuilder.directExchange(MqConstant.MESSAGE_TTL_EXCHANGE).durable(true).build(); }
- 绑定队列和交换机
/** * 死信队列绑定交换机 * @param visitorTtlQueue 死信队列 * @param messageTtlDirect 交换机 * @return Binding */ @Bean public Binding bindVisitorTtlDirectExchange(@Qualifier("visitorTtlQueue") Queue visitorTtlQueue, @Qualifier("messageTtlDirect") DirectExchange messageTtlDirect){ return BindingBuilder.bind(visitorTtlQueue).to(messageTtlDirect).with(MqConstant.TTL_ROUTE_KEY); } /** * 转发队列绑定交换机 * @param visitorTtlChangeQueue * @param visitorDirectExchange * @return Binding */ @Bean public Binding bindVisitorTtlChangeExchange(@Qualifier("visitorTtlChangeQueue") Queue visitorTtlChangeQueue, @Qualifier("visitorDirectExchange") DirectExchange visitorDirectExchange){ return BindingBuilder.bind(visitorTtlChangeQueue).to(visitorDirectExchange).with(MqConstant.TTL_CHANGE_ROUTE_KEY); }
- 发送死信消息
rabbitTemplate.convertAndSend(mqMessage.getExchange(),mqMessage.getRoutingKey(),mqMessage, message -> { // 设置延迟毫秒值 message.getMessageProperties().setExpiration(String.valueOf(delayTimes)); return message; }, new CorrelationData(mqMessage.getId()));
- 监听转发队列
@Component @AllArgsConstructor @Slf4j public class VisitorEventDrive { @RabbitHandler @RabbitListener(queues = MqConstant.VISITOR_TTL_CHANGE_QUEUE) public void receiveEvent(Message message, Channel channel, MqMessage mqMessage){ try { //doSomeThing }catch (Exception e) { } } }