目录
rabbitmq-直接交换模式
rabbitmq-广播模式- 路由
rabbitmq-广播模式- 交换机
rabbitmq-转发模式
rabbitmq-延迟消费 在【队列上】
rabbitmq-延迟消费 在【消息上】
源码地址:https://gitee.com/caiwang/rabbitmq-project
在介绍具体的实现思路之前,我们先来介绍一下RabbitMQ的两个特性,
一个是Time-To-Live Extensions,另一个是Dead Letter Exchanges
Time-To-Live Extensions RabbitMQ允许我们为消息或者队列设置TTL(time to live),也就是过期时间 TTL表明了一条消息可在队列中存活的最大时间,单位为毫秒 也就是说,当某条消息被设置了TTL或者当某条消息进入了设置了TTL的队列时,这条消息会在经过TTL秒后“死亡”,成为Dead Letter 如果既配置了消息的TTL,又配置了队列的TTL,那么较小的那个值会被取用
Dead Letter Exchange 在RabbitMQ中,一共有三种消息的“死亡”形式 消息被拒绝。通过调用basic.reject或者basic.nack并且设置的requeue参数为false。也就是:requeue=false 消息因为设置了TTL而过期。 消息进入了一条已经达到最大长度的队列 如果队列设置了Dead Letter Exchange(DLX),那么这些Dead Letter就会被重新publish到Dead Letter Exchange 通过Dead Letter Exchange路由到其他队列
为了方便接下来的测试,我们将delay_queue_per_message_ttl以及delay_queue_per_queue_ttl的DLX配置分两个目录进行演示,
且过期的消息都会通过DLX转发到delay_process_queue
在 commom中创建
DelaySenderEessageCommon
public class DelaySenderEessageCommon {
/**
* ========= delay_queue_per_message_ttl:TTL配置在【消息上】的缓冲队列。===========
*/
/**
* TTL配置在【消息上】的缓冲队列
*/
public static final String DELAY_EXCHANGE_QUEUE_TTL_NAME = "delay_exchange_queue_ttl_name";
/**
* TTL配置在【队列上】的缓冲队列
*/
public static final String DELAY_EXCHANGE_SLOW_NAME = "delay_exchange_slow_name";
/**
* 【消息上】 实际消费队列
*/
public static final String DELAY_EXCHANGE_QUEUE_NAME = "delay_exchange_queue_name";
}
在项目【rabbitmq-sender-project05】中创建
DelaySenderEessageConfig
@Configuration
public class DelaySenderEessageConfig {
/**
* 功能描述 delay_queue_per_message_ttl TTL配置在【消息上】的缓冲队列
* @param
* @return org.springframework.amqp.core.Queue
* @author cailu
* @date 2020/3/30 17:11
*/
@Bean
public Queue delayQueuePerMessageTTL() {
return QueueBuilder.durable(DelaySenderEessageCommon.DELAY_EXCHANGE_QUEUE_TTL_NAME)
//x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
//x-dead-letter-routing-key 声明了这些死信在转发时携带的DLK名称
// DLX,dead letter发送到的exchange 延迟交换名称
.withArgument("x-dead-letter-exchange", DelaySenderEessageCommon.DELAY_EXCHANGE_SLOW_NAME)
// DLK dead letter携带的routing key 每个消息的延迟队列名称
.withArgument("x-dead-letter-routing-key", DelaySenderEessageCommon.DELAY_EXCHANGE_QUEUE_NAME)
.build();
}
/**
* 功能描述 首先,我们需要配置DLX
* @param
* @return org.springframework.amqp.core.DirectExchange
* @author cailu
* @date 2020/3/30 17:23
*/
@Bean
DirectExchange delayExchange() {
return new DirectExchange(DelaySenderEessageCommon.DELAY_EXCHANGE_SLOW_NAME);
}
/**
* 功能描述 delay_process_queue:实际消费队列
* @param
* @return org.springframework.amqp.core.Queue
* @author cailu
* @date 2020/3/30 17:22
*/
@Bean
Queue delayProcessQueue() {
return QueueBuilder.durable(DelaySenderEessageCommon.DELAY_EXCHANGE_QUEUE_NAME)
.build();
}
/**
* 功能描述 然后再将该DLX绑定到实际消费队列即delay_process_queue上。
* 这样所有的死信都会通过DLX被转发到delay_process_queue:
* @param delayProcessQueue 延迟处理队列
* @param delayExchange 延迟交换
* @return org.springframework.amqp.core.Binding
* @author cailu
* @date 2020/3/30 17:23
*/
@Bean
Binding dlxBinding(Queue delayProcessQueue, DirectExchange delayExchange) {
return BindingBuilder.bind(delayProcessQueue)
.to(delayExchange)
.with(DelaySenderEessageCommon.DELAY_EXCHANGE_QUEUE_NAME);
}
}
创建[延迟消费 设置消息的TTL属性]
ExpirationMessagePostProcessor
public class ExpirationMessagePostProcessor implements MessagePostProcessor {
// 毫秒
private final Long ttl;
public ExpirationMessagePostProcessor(Long ttl) {
this.ttl = ttl;
}
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties()
// 设置per-message的失效时间
.setExpiration(ttl.toString());
return message;
}
}
创建消息模板
DelaySenderEessageTemplate:
@Component
public class DelaySenderEessageTemplate {
private Logger logger = (Logger) LoggerFactory.getLogger(this.getClass());
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 功能描述 配置在消息上的延迟消费
* 在发布消息时 一定要注意 发布的消息 是在【延迟队列】中的【缓冲队列】里
* 通过TTL的绑定 使其在 死信队列 中 按照时间 自动消化掉
* @return void
* @author cailu
* @date 2020/3/31 11:19
*/
public void delayQueuePerMessageTTL() {
//延迟的时间 毫秒
long expiration = 30000;
rabbitTemplate.convertAndSend(DelaySenderEessageCommon.DELAY_EXCHANGE_QUEUE_TTL_NAME,
(Object) ("Message From delay_exchange_queue_ttl_name with expiration " + expiration),
new ExpirationMessagePostProcessor(expiration));
}
}
现在我们启动项目:观察mq后台
我们发送一条信息
可以看到 首先 我们发送的信息在delay_exchange_queue_ttl_name 中,等待单位时间内(也就是需要延迟的时间),那么这条消息就会自动转发到delay_exchange_queue_name中
这里的转发时间是由ExpirationMessagePostProcessor类中ExpirationMessagePostProcessor管理的,
可以自定义时间,我这里方便演示,直接在代码中定义了,后期跟进业务,可以更改为动态传入
现在已经做了延迟了,也就是说 我们真正的消费队列 是:delay_exchange_queue_name,
而delay_exchange_queue_ttl_name 只是临时的,或者说是我们在单位时间内被定义出来的,
其实这里有很多中用法,具体用法可以根据具体业务而定,这里只做展示
接下来 我们来创建消费者
在项目【rabbitmq-receiver-project05】中创建
DelaySenderEessageReceiver:
@Component
public class DelaySenderEessageReceiver {
/**
* 功能描述 监听 在【消息上】 延迟消费
* @param str 内容
* @return void
* @author cailu
* @date 2020/3/31 15:14
*/
@RabbitListener(queues = DelaySenderEessageCommon.DELAY_EXCHANGE_QUEUE_NAME)
public void processQueue(String str) {
System.out.println("Receive---delay_exchange_queue_name:========================:" + str.toString());
}
}
启动消费者 观察日志
可以看到刚才的延迟的两条 全部被消费掉
至此 rabbitmq-延迟消费 在【队列上】完毕