RabbitMQ+SpringBoot使用插件实现延迟队列

rabbitmq实现延迟队列有两种方式,死信队列和插件形式.。

安装RabbitMQ插件

目的:实现队列的延迟发送功能

插件:rabbitmq-delayed-message-exchange

需要使用含有交换机的模型队列,延迟时间是设置在交换机上的。

  1. 下载插件,配置rabbit

下载对应的版本**rabbitmq-delayed-message-exchange**

我这次使用的版本:RabbitMQ 3.8.9 Erlang 23.1

插件版本:rabbitmq_delayed_message_exchange 3.8.0

暂时没发现问题,最好还是对应上版本号。

配置rabbitMq延时队列 java_队列

ps:在拷贝插件的时候,一定要注意位置,因为你的服务器上可能不只有一份rabbitmq程序。

  1. 启动插件
[root@localhost plugins]# rabbitmq-plugins enable rabbitmq_delayed_message_exchange
Enabling plugins on node rabbit@localhost:
rabbitmq_delayed_message_exchange
The following plugins have been configured:
  rabbitmq_delayed_message_exchange
  rabbitmq_management
  rabbitmq_management_agent
  rabbitmq_web_dispatch
Applying plugin configuration to rabbit@localhost...
The following plugins have been enabled:
  rabbitmq_delayed_message_exchange

started 1 plugins.
  1. 重启rabbitmq
[root@localhost bin]# rabbitmqctl stop
Stopping and halting node rabbit@localhost ...
[root@localhost bin]# rabbitmq-server -detached
  1. 查看插件列表
[root@localhost bin]# rabbitmq-plugins list
Listing plugins with pattern ".*" ...
 Configured: E = explicitly enabled; e = implicitly enabled
 | Status: * = running on rabbit@localhost
 |/
[  ] rabbitmq_amqp1_0                  3.8.9
[  ] rabbitmq_auth_backend_cache       3.8.9
[  ] rabbitmq_auth_backend_http        3.8.9
[  ] rabbitmq_auth_backend_ldap        3.8.9
[  ] rabbitmq_auth_backend_oauth2      3.8.9
[  ] rabbitmq_auth_mechanism_ssl       3.8.9
[  ] rabbitmq_consistent_hash_exchange 3.8.9
[E*] rabbitmq_delayed_message_exchange 3.8.0
[  ] rabbitmq_event_exchange           3.8.9
[  ] rabbitmq_federation               3.8.9
[  ] rabbitmq_federation_management    3.8.9
[  ] rabbitmq_jms_topic_exchange       3.8.9
[E*] rabbitmq_management               3.8.9
[e*] rabbitmq_management_agent         3.8.9
[  ] rabbitmq_mqtt                     3.8.9
[  ] rabbitmq_peer_discovery_aws       3.8.9
[  ] rabbitmq_peer_discovery_common    3.8.9
[  ] rabbitmq_peer_discovery_consul    3.8.9
[  ] rabbitmq_peer_discovery_etcd      3.8.9
[  ] rabbitmq_peer_discovery_k8s       3.8.9
[  ] rabbitmq_prometheus               3.8.9
[  ] rabbitmq_random_exchange          3.8.9
[  ] rabbitmq_recent_history_exchange  3.8.9
[  ] rabbitmq_sharding                 3.8.9
[  ] rabbitmq_shovel                   3.8.9
[  ] rabbitmq_shovel_management        3.8.9
[  ] rabbitmq_stomp                    3.8.9
[  ] rabbitmq_top                      3.8.9
[  ] rabbitmq_tracing                  3.8.9
[  ] rabbitmq_trust_store              3.8.9
[e*] rabbitmq_web_dispatch             3.8.9
[  ] rabbitmq_web_mqtt                 3.8.9
[  ] rabbitmq_web_mqtt_examples        3.8.9
[  ] rabbitmq_web_stomp                3.8.9
[  ] rabbitmq_web_stomp_examples       3.8.9

SpringBoot实现

这里说明一下,要看你所在的公司提供的rabbitmq账号是否有创建交换机和队列的权限。

  • 有权限>可以在代码中使用注解当交换机或队列不存在时创建并配置延迟模式;
  • 没权限>需要让运维在控制台上给你创建好支持延迟模式的交换机,创建队列绑定上对应交换机。

这里说的“延迟模式”只有在正确安装延迟插件才会有得选。

以下我提供在代码管理交换机的方式。

  1. rabbitmq关键字
public interface RabbitConsts {
    /**
     * 会员过期交换机
     */
    final static String EXCHANGE_DELAY_ACCOUNT_EXPIRE = "exchange.delay.account.expire";
    /**
     * 会员过期队列
     */
    final static String QUEUE_DELAY_ACCOUNT_EXPIRE = "queue.delay.account.expire";
    /**
     * 会员过期路由
     */
    final static String KEY_DELAY_ACCOUNT_EXPIRE = "key.delay.account.expire";
}
  1. 消费端 —— 添加配置类,管理交换机、队列和路由key
@Slf4j
@Configuration
public class RabbitMqConfig {
	@Bean
    public TopicExchange delayExchange(){
        TopicExchange exchange = new TopicExchange(RabbitConsts.EXCHANGE_DELAY_ACCOUNT_EXPIRE, true, false
        ,        new HashMap<String, Object>(){{put("x-delayed-type", "topic");}});
        exchange.setDelayed(true);
        return exchange;
    }
    @Bean
    public Queue lazyQueue(){
        return new Queue(RabbitConsts.QUEUE_DELAY_ACCOUNT_EXPIRE, true);
    }
    @Bean
    public Binding lazyBinding(){
        return BindingBuilder.bind(lazyQueue()).to(delayExchange()).with(RabbitConsts.KEY_DELAY_ACCOUNT_EXPIRE);
    }
}
  1. 消费端 —— 监听
@Service
@Slf4j
public class AccountQueueHandler {
    @RabbitListener(queues = RabbitConsts.QUEUE_DELAY_ACCOUNT_EXPIRE )
    public void receive1(Object message){
        System.out.println("receive 1:"+message);
    }
}
  1. 生产端 —— 发送消息
public void sendLazy(String data,int delay){
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> log.info("消息发送成功:correlationData({}),ack({}),cause({})", correlationData, ack, cause));
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> log.info("消息丢失:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange, routingKey, replyCode, replyText, message));

        //发送消息时指定 header 延迟时间
        rabbitTemplate.convertAndSend(RabbitConsts.EXCHANGE_DELAY_ACCOUNT_EXPIRE, RabbitConsts.KEY_DELAY_ACCOUNT_EXPIRE, data,
                new MessagePostProcessor() {
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        //设置消息持久化
                        message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                        message.getMessageProperties().setHeader("x-delay", 6000);
//                        message.getMessageProperties().setDelay(6000);
                        return message;
                    }
                });
    }

设置延迟时间的两种方式是一样的:这里的时间单位是 毫秒

1.  message.getMessageProperties().setDelay(6000);
2.  message.getMessageProperties().setHeader("x-delay", 6000);

setDelay的内部就是第二种的写法;

至此延迟队列的效果已经实现了。

浅谈延迟队列原理

创建好支持延迟模式的交换机后,控制台是这样的:

配置rabbitMq延时队列 java_队列_02

messages delayed 就是延迟的消息数量。

然后,发送端每秒发送一次,试着观察队列发现队列一直是空的,故此感觉延迟的消息是堆放在交换机。

到时间了才推送到队列,然后被消费者接收。