死信队列:就是正常消费者在一定时间内没有进行消费的消息会发送到死信队列中,而会有消费者在监听死信队列。消费者在消息被拒绝、消息TTL过期、队列达到最大长度这三种情况下,消息会被消费者转发到死信队列。
延迟队列:其实就是死信队列中消息过期的特殊情况
延迟队列应用场景:
死信队列实现:在以上场景中,就可能需要在RabbitMq中有多个不同过期时间的需求,在这种情况下通过死信队列可以初步实现如下:在定义队列时并不设施固定的消息过期时间,而在生产者发送消息时,将过期时间作为参数传递到队列中,这样就可以实现多种不同过期时间的需求。
对于以上方法存在一个问题:死信队列这种多个过期时间共存的情况下如果过期时间长短刚好设置从小到大的添加顺序时,可能没有问题,但是这在实际应用中不可能的。假设第一个过期时间为20s,第二个过期时间为2s,这种情况下就会出现2s之后第二条消息并不会执行到期的操作,而会等到第一条的过期时间到了之后才会执行,这样在实际业务中就会出问题。原因如下:
综上所述:就需要安装RabbitMq的延迟插件,新增一种交换机的模式来解决这个问题!
1、首先一定一定一定要确定好自己的RabbitMq的版本是多少,这很重要,然后再官网打开下载即可。如果你的rabbitmq版本是3.7.x,那就下载v3.8x版本即可,下载连接:https://www.rabbitmq.com/community-plugins.html
1、将.ez压缩包放到rabbitmq的plugins目录中\RabbitMQ Server\rabbitmq_server-3.7.7\plugins
2、进入sbin目录,打开管理员控制台,输入如下命令,显示类似信息即可:
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
3、重新双击sbin目录下的rabbitmq-server.bat文件,启动rabbitmq服务。
注意:如果前面下载的插件版本和rabbitmq版本不匹配,就会导致无法启动,出现闪退或者显示BOOT FAILED,删掉刚才的插件,重新执行上面流程即可。
4、启动服务之后打开rabbitmq管理官新增交换机即可看到新的交换模式。
前置条件是在Docker中部署过RabbitMq。
1、打开你的远程工具,首先查看docker中已有的容器,主要是为了查看rabbitmq的容器ID
2、将本地下载好的压缩包传到服务器某文件夹下,然后将其复制到Docker中的RabbitMq容器中的plugins文件夹下。
docker cp /home/rabbitmq_delayed_message_exchange-3.8.0.ez a687ef46141b:/plugins
3、进入容器查看该目录下是否有该压缩包。
进入容器命令:(通过容器号或者容器名)
docker exec -it a687ef46141b bash
4、同样在容器中的命令行执行一下命令添加插件。
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
5、退出容器,重启该容器。
6、在管理端即同样可以看到新增了一种交换机模式。
总结:以上就是RabbitMQ的延迟插件的安装过程!
成功安装RabbitMQ的延迟插件之后,我们就可以尝试写一个延迟队列来验证一下是否可以解决上述问题。
首先我们的测试环境是在一个Springboot的框架下完成!
1、最先写配置类
/**
* 定义延迟交换机
*/
@Configuration
public class RabbitMQDelayedConfig {
//队列
private static final String DELAYQUEUE = "delayedqueue";
//交换机
private static final String DELAYEXCHANGE = "delayedExchange";
@Bean
public Queue delayqueue(){return new Queue(DELAYQUEUE);}
//自定义延迟交换机
@Bean
public CustomExchange delayedExchange(){
Map arguments = new HashMap<>();
arguments.put("x-delayed-type","direct");
/**
* 1、交换机名称
* 2、交换机类型
* 3、是否需要持久化
* 4、是否需要自动删除
* 5、其他参数
*/
return new CustomExchange(DELAYEXCHANGE,"x-delayed-message",true,false,arguments);
}
//绑定队列和延迟交换机
@Bean
public Binding delaybinding(){
return BindingBuilder.bind(delayqueue()).to(delayedExchange()).with("sectest").noargs();
}
}
2、先写生产者
/**
* 基于插件的延迟队列
* 消息生产者
*/
@Service
@Slf4j
public class DelayMQSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendmsg(String message,Integer delaytime){
log.info("当前时间:{},发送时长{}信息给延迟队列:{}",new Date().toString(),delaytime,message);
rabbitTemplate.convertAndSend("delayedExchange","sectest",message,msg->{
//设置发送消息的延长时间 单位:ms
msg.getMessageProperties().setDelay(delaytime);
return msg;
});
}
}
3、再写消费者
/**
* 基于插件的延迟队列
* 消息的消费者
*/
@Service
@Slf4j
public class DelayMQReceiver {
@RabbitListener(queues = "delayedqueue")
public void receivemsg(Message messages){
String msg = new String(messages.getBody());
log.info("当前时间:{},接收时长信息给延迟队列:{}",new Date().toString(),msg);
}
}
4、进行测试
将模拟请求放在了一个简易的网页上,点击后输出如下结果,证明当先发送了20s延时的消息,再发送2s延时的消息,在2s后消息正常被消费,基于插件的延迟队列完美解决了问题。
【思考】:如果在实际业务场景中使用延迟队列的话,那就需要服务端在消息被消费之后主动告诉前端消费的结果,如果是这样的话,那么Ajxs的通信方式是单双工通信,只能前端主动访问后端并返回结果,后端无法主动发送消息,应该使用Websocket来进行通信才可,websocket是长连接,不同于http的短连接,可以实现全双工通信,前后端都可以主动发送消息。