RabbitMQ-延迟交换器

在之前的延迟场景中,消息放入延迟队列的时间都是相同的。比如京东下单后,最大等待24小时进行付款。如果超过24小时还没有付款,那么订单将会被取消。由于下单后使用固定的时间等待。直接采用延迟队列没有任何问题。那如果是会议预订系统的提前20分钟通知功能呢?比如产品经理预订一个会议订在11点,到了10点45分(提前15分钟)的时候就会通知所有参会人员做好准备。会议会在15分钟后开始。如果我们将此通知放入延迟队列。会是什么样子呢?

通过分析可以发现,如果将消息放入延迟队列,由于延迟队列是顺序消费的特性,即按照顺序一个一个的消费。以图为例,可以发现,排在队首的是延迟30秒,会被最先消费,而第一个消费完,第二个和第三个都已经过期了。这时候再通知已经没有意义了。采用延迟队列是不行的。

在RabbitMQ中,可以使用rabbitmq_delayed_message_exchange插件来实现。

这里和TTL方式有很大的不同就是TTL存放的是死信队列(deayquque),而这个插件存放的消息是在延迟交换器里(x-delayed-message-exchange)

  1. 生产者将消息和路由键发送至指定的延迟交换器上

  2. 延迟交换器将存储消息等待消息到期根据路由键绑定到自己的队列将把消息给它。

  3. 队列再把消息发给给监听它的消费者。

  4. 下载插件

下载地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/tag/3.8.9

  1. 安装及启用插件

将插件拷贝到rabbitMQ-server的安装路径:/usr/lib/rabbitmq/lib/rabbitmq_server-3.8.5/plugins/

sh 复制代码
cd /usr/lib/rabbitmq/lib/rabbitmq_server-3.8.5/plugins/

# 检查插件
[root@nullnull-os plugins]# find . -name "*delayed*"
./rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez

# 检查插件
rabbitmq-plugins list
# 启动插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange

启用插件过程

sh 复制代码
[root@nullnull-os plugins]# find . -name "*delayed*"
./rabbitmq_delayed_message_exchange-3.8.9-0199d11c.ez
[root@nullnull-os plugins]# rabbitmq-plugins list
Listing plugins with pattern ".*" ...
 Configured: E = explicitly enabled; e = implicitly enabled
 | Status: * = running on rabbit@nullnull-os
 |/
[  ] rabbitmq_amqp1_0                  3.8.5
[  ] rabbitmq_auth_backend_cache       3.8.5
[  ] rabbitmq_auth_backend_http        3.8.5
[  ] rabbitmq_auth_backend_ldap        3.8.5
[  ] rabbitmq_auth_backend_oauth2      3.8.5
[  ] rabbitmq_auth_mechanism_ssl       3.8.5
[  ] rabbitmq_consistent_hash_exchange 3.8.5
[  ] rabbitmq_delayed_message_exchange 3.8.9.0199d11c
[  ] rabbitmq_event_exchange           3.8.5
[  ] rabbitmq_federation               3.8.5
[  ] rabbitmq_federation_management    3.8.5
[  ] rabbitmq_jms_topic_exchange       3.8.5
[E*] rabbitmq_management               3.8.5
[e*] rabbitmq_management_agent         3.8.5
[  ] rabbitmq_mqtt                     3.8.5
[  ] rabbitmq_peer_discovery_aws       3.8.5
[  ] rabbitmq_peer_discovery_common    3.8.5
[  ] rabbitmq_peer_discovery_consul    3.8.5
[  ] rabbitmq_peer_discovery_etcd      3.8.5
[  ] rabbitmq_peer_discovery_k8s       3.8.5
[  ] rabbitmq_prometheus               3.8.5
[  ] rabbitmq_random_exchange          3.8.5
[  ] rabbitmq_recent_history_exchange  3.8.5
[  ] rabbitmq_sharding                 3.8.5
[  ] rabbitmq_shovel                   3.8.5
[  ] rabbitmq_shovel_management        3.8.5
[  ] rabbitmq_stomp                    3.8.5
[  ] rabbitmq_top                      3.8.5
[E*] rabbitmq_tracing                  3.8.5
[  ] rabbitmq_trust_store              3.8.5
[e*] rabbitmq_web_dispatch             3.8.5
[  ] rabbitmq_web_mqtt                 3.8.5
[  ] rabbitmq_web_mqtt_examples        3.8.5
[  ] rabbitmq_web_stomp                3.8.5
[  ] rabbitmq_web_stomp_examples       3.8.5
[root@nullnull-os plugins]# rabbitmq-plugins enable rabbitmq_delayed_message_exchange
Enabling plugins on node rabbit@nullnull-os:
rabbitmq_delayed_message_exchange
The following plugins have been configured:
  rabbitmq_delayed_message_exchange
  rabbitmq_management
  rabbitmq_management_agent
  rabbitmq_tracing
  rabbitmq_web_dispatch
Applying plugin configuration to rabbit@nullnull-os...
The following plugins have been enabled:
  rabbitmq_delayed_message_exchange

started 1 plugins.
[root@nullnull-os plugins]# 

如果在未启动的情况下安装插件,在重启后才能生效

systemctl restart rabbitmq-server

maven导入

xml 复制代码
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.2.8.RELEASE</version>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <version>2.2.7.RELEASE</version>
            <scope>test</scope>
        </dependency>

springBoot的配制

application.yml

yaml 复制代码
spring:
  application:
    name: delayedQueue
  rabbitmq:
    host: node1
    port: 5672
    virtual-host: /
    username: root
    password: 123456

主入口

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DelayExchangeApplication {
  public static void main(String[] args) {
    SpringApplication.run(DelayExchangeApplication.class, args);
  }
}

队列配制

java 复制代码
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
@RabbitListener
@ComponentScan("com.nullnull.learn")
public class DelayConfig {

  @Bean
  public Queue queue() {
    Queue queue = new Queue("delay.qu", false, false, true, null);
    return queue;
  }

  @Bean
  public Exchange exchange() {
    Map<String, Object> argument = new HashMap<>();
    argument.put("x-delayed-type", ExchangeTypes.FANOUT);
    Exchange exchange = new CustomExchange("delay.ex", "x-delayed-message", true, false, argument);
    return exchange;
  }

  @Bean
  public Binding bind() {
    return BindingBuilder.bind(queue()).to(exchange()).with("delay.rk").noargs();
  }
}

controller代码

java 复制代码
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.core.MessagePropertiesBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;

@RestController
public class DelayController {

  @Autowired private AmqpTemplate template;

  @RequestMapping("/notify/{seconds}")
  public String toMeeting(@PathVariable Integer seconds) throws Exception {

    MessageProperties prop = MessagePropertiesBuilder.newInstance()
            //编码
            .setContentEncoding(StandardCharsets.UTF_8.name())
            //延迟时间
            .setHeader("x-delay", seconds * 1000)
            .build();
    

    byte[] meetContainer = (seconds + "秒后通知部门会议").getBytes(StandardCharsets.UTF_8);
    Message msg = new Message(meetContainer, prop);

    template.convertAndSend("delay.ex", "delay.rk", msg);
    return "已经设定好了通知";
  }
}

监听器代码

java 复制代码
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;


@Component
public class DelayListener {
  @RabbitListener(queues = "delay.qu")
  public void MeetingAlarm(Message msg, Channel channel) throws Exception {
    String alarmMsg = new String(msg.getBody(), msg.getMessageProperties().getContentEncoding());
    System.out.println("收到提醒:" + alarmMsg);
  }
}

启动测试

分钟别在浏览器中输入:

http://127.0.0.1:8080/notify/20

http://127.0.0.1:8080/notify/15

http://127.0.0.1:8080/notify/7

http://127.0.0.1:8080/notify/2

观察控制台输出:

sh 复制代码
收到提醒:2秒后通知部门会议
收到提醒:7秒后通知部门会议
收到提醒:15秒后通知部门会议
收到提醒:20秒后通知部门会议

查看交换器

相关推荐
一张假钞18 分钟前
Zookeeper单机操作
分布式·zookeeper·云原生
未命名冀2 小时前
消息中间件面试
面试·kafka·rabbitmq
FF在路上5 小时前
RabbitMQ高级特性
分布式·微服务·rabbitmq
啊晚7 小时前
ASP.NET Core - 缓存之分布式缓存
分布式·缓存·asp.net
斯普信专业组9 小时前
RabbitMQ 高可用方案:原理、构建与运维全解析
运维·分布式·rabbitmq
huapiaoy9 小时前
RabbitMQ的工作模式
分布式·rabbitmq
黄名富10 小时前
Kafka 分区管理
java·分布式·缓存·kafka
抛砖者10 小时前
01、kafka知识点综合
分布式·kafka·cap·kafka面试·kafka优化·kafka架构
十六ᵛᵃᵉ10 小时前
day08_Kafka
分布式·kafka
大梦谁先觉i10 小时前
RocketMQ、Kafka、RabbitMQ,如何选型?
kafka·rabbitmq·rocketmq