应用场景:饿了么下订单,若没有支付,会显示个支付倒计时,若倒计时结束,还没支付,则该消息会被送入延时队列处理 两种实现方式:1.基于参数TTL(可以在消息设置或者在队列设置),并绑定到对应的死信交换机 2.使用插件,获得一个有延迟功能的Direct交换机 两种实现方式的区别:
基于TTL的实现
在消息中设置延时

AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("10000").build();
channel.channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",propertis,message.getBytes());
在队列中设置延时
放入QA中消息经过10s后会被通过绑定的死信交换机Y,送入死信队列(延迟队列)中。至于没有过期的消息如何处理,尚未可知。 
@Bean("queueB")
public Queue queueB(){
Map<String, Object> args = new HashMap<>(3);
args.put("x-dead-letter-exchange", Y_DEAD_LETTER_EXCHANGE);
args.put("x-dead-letter-routing-key", "YD");
args.put("x-message-ttl", 40000);
return QueueBuilder.durable(QUEUE_B).withArguments(args).build();
}
这种情况,如果要设置1h,2h,3h延时的队列就需要分别创建对应的队列,很麻烦
基于插件的实现
通过安装延时插件,可以获得一个有延时功能的Direct交换机,生产者P发布的消息(设置了延时)会被放入该交换机,等待过期之后,送入绑定的延时队列中,由C消费。至于没有过期的消息如何处理,尚未可知。

@Configuration
public class DelayedQueueConfig {
public static final String DELAYED_QUEUE_NAME = "delayed.queue";
public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
public static final String DELAYED_ROUTING_KEY = "delayed.routingkey";
@Bean
public Queue delayedQueue() {
return new Queue(DELAYED_QUEUE_NAME);
}
@Bean
public CustomExchange delayedExchange() {
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");
return new CustomExchange(DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false,args);
}
@Bean
public Binding bindingDelayedQueue(@Qualifier("delayedQueue") Queue queue, @Qualifier("delayedExchange") CustomExchange delayedExchange) {
return BindingBuilder.bind(queue).to(delayedExchange).with(DELAYED_ROUTING_KEY).noargs();
}
}
问题
基于设置TTL的方式,消息是放在队列中的,如果前置消息没有被消费的话,后面的消息就不会被消费,也就是说,如果第一个消息设置延时20s,第二个消息设置延时2s,则因为第一个消息迟迟没有被处理,等到它过期,第二个消息早就过期了,故实际情况不应采用这种方式,所以应该采取基于插件的方式
|