springboot+rabbitMQ实现消息延迟

本文介绍了为何需要消息队列并以快递配送为例解释其作用,如解耦、异步和削峰。接着讲解了RabbitMQ的基本原理,强调了交换机和标签在消息传递中的角色。然后详细描述了在Windows上安装RabbitMQ和延迟消息插件的步骤,并提供了相关命令。最后,展示了如何在SpringBoot应用中配置和使用RabbitMQ实现延迟消息的发送和接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

消息队列

为什么需要消息队列

所有东西的出现都存在一定的原因,消息队列一定是解决了很多问题,所以它才会存在。这个应该也是当我们了解到一个新的知识下的时候首先需要去做的是明确为什么存在这个技术,这个技术可以解决哪些问题,这样再去进行相关的学习,这样你才会对于它的作用理解的更加深刻!

为什么需要消息队列呢?这个可以从一个实际的例子出发:

取快递的例子,菜鸟驿站就是快递员把东西放在菜鸟驿站,我们有时间再去拿,菜鸟驿站就可以理解成一个消息队列。它实现了什么,就是消息队列的作用。

1、解耦:如果没有这样的菜鸟驿站,需要收货人和快递员约定时间,去完成快递交易。这样对于收货人以及快递小哥都需要协调互相的时间以及地点等,存在很大的耦合,但是很明显,菜鸟驿站就直接解决了这个问题。

2、异步:快递小哥把东西放在菜鸟驿站就可以继续去送其他人的快递,而不需要等待把东西送到收货人手中再去进行接下来的工作。

3、 削峰:当快递很多的时候,不同的多个快递让我去不同的地方拿快递,而菜鸟驿站实现了我去一个地方同时拿到多个快递,当然就削峰了。就会存在一个缓冲的感觉。

关于异步我有话说:

消息队列不能仅仅理解成一个放消息的东西。所谓的消息是一个广义的。

设想一个情景:在双十一的前提下,用户提交订单之后,为没有支付的订单设定一个半个小时过期时间。在半个小时内支付了订单就会使得订单生效,但是对于没有在半个小时内支付的,订单到半个小时会失效,并且取消之间预减的库存。还有淘宝

这些场景都可以用到消息延迟!!!

rabbitmq支持延迟消息但不支持定时消息

rabbitMq简单原理

在这里插入图片描述

过程

在这里插入图片描述
整个过程就是生产者发送一个带上标签的消息发给交换机上,交换机以及标签放到相应的queue中,消费者从队列中获得消息的过程。

**注意:**这里的消费者从队列中获得相关信息的时候,队列不是讲所有的消息都发送给所有连接的消费者,而是采用轮询的方式轮流发送。所以RabbitMQ不支持广播消费。

所谓标签,其实专业一点就是生产者给交换机发送消息的时候要附带一个RoutingKey。而在交换机和队列联通的链路上每个都有各自的BindingKey,只有RoutingKey以及BindingKey匹配的时候才会放到相应的队列中。(但是不同的交换机机制不同)

交换机种类

direct:此类交换机当仅当两个完全匹配上才会放到相应的队列中

topic:允许模糊匹配,存在通配符#以及*

fanout:忽略匹配,会发送到该交换机连接的所有的queue

headers:不依据RoutingKey,而是依据发送消息的headers

rabbitMQ消息队列实现收到延迟消息

之前在ubuntu上安装过,还搭过相关的集群,写的是很完整的,可以参考:

今天在windows10进行安装:

安装erlang

参考别人的文章:https://blog.youkuaiyun.com/linsongbin1/article/details/80170487

安装rabbitMq

参考别人的文章:https://blog.youkuaiyun.com/linsongbin1/article/details/80170567

安装一个rabbitMQ延迟消息的一个插件

https://dl.bintray.com/rabbitmq/community-plugins/3.7.x/rabbitmq_delayed_message_exchange/rabbitmq_delayed_message_exchange-20171201-3.7.x.zip

上面是地址,注意要和rabbitmq版本进行匹配!

几个用得到的命令

这里存在有很多的重启rabbitMQ服务的需求,因此罗列一下相关的命令(对于windows下需要在安装目录下的sbin下输入相关的命令哦!):

开启服务:rabbitmq-server -detached

停止服务:rabbitmqctl stop

集成rabbitMq

RabbitMqConfig.class

关于这个配置文件主要就是为了保证rabbitmq的连接保证。

@Configuration
@ConfigurationProperties(prefix = "spring.rabbitmq")
public class RabbitMqConfig {
    private String host;
    private int port;
    private String userName;
    private String password;

    @Bean
    public ConnectionFactory connectionFactory(){
        CachingConnectionFactory cachingConnectionFactory=new CachingConnectionFactory();
        cachingConnectionFactory.setUsername(userName);
        cachingConnectionFactory.setPort(port);
        cachingConnectionFactory.setHost(host);
        cachingConnectionFactory.setPassword(password);
        cachingConnectionFactory.setVirtualHost("/");
        cachingConnectionFactory.setPublisherConfirms(true);
        return cachingConnectionFactory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(){
        RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory());
        return rabbitTemplate;
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

QueueConfig.class

@Configuration
public class QueueConfig {

   //定义一个交换机
    @Bean
    public CustomExchange delayExchange(){
        Map<String,Object> args=new HashMap<>();
        args.put("x-delay-type","direct");
        return new CustomExchange("test_exchange","x-delayed-message",true,false,args);
    }
//定义一个队列
    @Bean
    public Queue queue(){
       Queue queue=new Queue("test_queue_1",true);
       return queue;
    }
//绑定这个队列以及这个交换机(bindingKey=test_queue_1)
    @Bean
    public Binding binding(){
       return BindingBuilder.bind(queue()).to(delayExchange()).with("test_queue_1").noargs();
    }
}

在这里插入图片描述

上面就是完成了以上工作,test_queue_1就是这个BindingKey

MessageServiceImpl .class

@Service
public class MessageServiceImpl {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage(String queueName,String msg){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("消息发送时间:"+sdf.format(new Date()));
        //向名字是test_exchange发送msg这个消息,并且给这个消息加个头x-delay
        rabbitTemplate.convertAndSend("test_exchange", queueName, msg, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //给消息加上header,表明是一个延迟消息,并且设置过期时间3秒
                message.getMessageProperties().setHeader("x-delay",3000);
                return message;
            }
        });
    }

}

上面所谓的那个queueName,其实就是RoutingKey,需要生产者指定。

所以关于延迟消息其实上一个消息的header保证的!

在这里插入图片描述

MessageReceiver.class

@Component
public class MessageReceiver {
    @RabbitListener(queues = "test_queue_1")
    public void receiver(String msg){
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("消息接收时间:"+sdf.format(new Date()));
        System.out.println("接收到的消息:"+msg);
    }
}

在这里插入图片描述

这个消息的接受者连接到test_queue_1这个队列,可以连接多个。

sendController.class

@RestController
public class sendController {
    @Autowired
    private MessageServiceImpl messageService;

    @GetMapping("/send/{msg}")
    public void send(@PathVariable("msg")String msg){
            messageService.sendMessage("test_queue_1",msg);
    }
}

http://localhost:8071/rabbitmq/send/xxxxx

多测试几次就会成功!会在3秒后收到消息!

eService;

@GetMapping("/send/{msg}")
public void send(@PathVariable("msg")String msg){
        messageService.sendMessage("test_queue_1",msg);
}

}


http://localhost:8071/rabbitmq/send/xxxxx

多测试几次就会成功!会在3秒后收到消息!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值