Spring Boot 整合——RabbitMQ配置延时队列和消息重试

延时队列

通常我们会有一些额外的需求,一些队列中的消息并不是需要立即需要被消费掉的。这个时候我们需要将消息延迟处理。为了处理这些延时的消息。这个时候就是死信路由发挥作用的时候了。

要实现延迟队列,首先我们需要了解RabbitMQ中对于和消息相关的概念:消息的TTL死信Exchange

TTL

消息的TTL指的是消息的存活时间(Time To Live)。RabbitMQ中可以对队列或者每一个消息设置单独的存活时间。当消息在队列中存在的时间超过这个设定值之后,系统会认为这个消息死了,这就写消息被称为"死信"。

要想设置消息的超时时间,我们可以配置消息中expiration字段或者配置队列的x-message-ttl参数。

当消息超过时间依旧没被消费掉,它会变成死信,但是这个消息并不会被删除或者丢弃。但是单靠死信,我们是没法实现延迟队列的。这里就需要死信Exchange来配合

死信Exchange

Exchange 之前我们已经介绍过了,而死信路由指的就是死信最后被转发到的路由地址。死信路由和普通的路由没有区别,只是某一个设置了死信路由的队列中存在消息过期的时候,会将消息转发过来。

整个死信Exchange的流程

超时
用户
queue
死信Exchange
用户消费
死信的queue
用户消费死信
组件的配置

根据之前的流程图我们可以知道要想实现延时消息我们需要以下内容

  • 一套接收消息的处理配置
  • 一套被订阅的死信的处理配置
  • 超时时间的设置

正常处理配置

根据之前的流程,这个队列里的消息是不需要被消费的。
等待队列中的消息超时后消息被推送至死信队列后,消费端在死信队列再进行消费。这里我们配置了4000毫秒的超时时间。

    /**
     * 延时队列
     * 发送到该队列的message会在一段时间后过期进入到delay_process_queue
     * 队列里所有的message都有统一的失效时间
     */
    public static String DELAY_QUEUE   = "delay.queue";

    /**
     * 延时的交换器
     */
    public static String DELAY_EXCHANGE = "delay.queue.exchange";
    /**
     * 超时时间
     */
    public static Long QUEUE_EXPIRATION = 4000L;
    
    /**
     * 配置延时交换器
     * @return
     */
    @Bean
    DirectExchange delayExchange() {
        return new DirectExchange(DELAY_EXCHANGE);
    }
    
    /**
     * 配置延时队列
     * @return
     */
    @Bean
    public Queue delayQueue() {
        return QueueBuilder.durable(DELAY_QUEUE)
                // DLX,dead letter发送到的exchange ,设置死信队列交换器到处理交换器
                .withArgument("x-dead-letter-exchange", PROCESS_EXCHANGE)
                // dead letter携带的routing key,配置处理队列的路由key
                .withArgument("x-dead-letter-routing-key", PROCESS_QUEUE)
                // 设置过期时间
                .withArgument("x-message-ttl", QUEUE_EXPIRATION)
                .build();
    }
    
    /**
     * 将delayQueue2绑定延时交换机中,routingKey为队列名称
     * @return
     */
    @Bean
    Binding queueTTLBinding() {
        return BindingBuilder
                .bind(delayQueue())
                .to(delayExchange())
                .with(DELAY_QUEUE);
    }

在正常接收消息的队列中,进行的特殊配置。
为队列添加了x-dead-letter-exchange,x-dead-letter-routing-key,
x-message-ttl这几个参数保证了消息超时后的死亡,并且保证了消息在死亡后
能够推送到真正被订阅的死信队列。

死信队列配置

    /**
     * 实际消费队列
     * message失效后进入的队列,也就是实际的消费队列
     */
    public static String PROCESS_QUEUE = "process.queue";

    /**
     * 处理的交换器
     */
    public static String PROCESS_EXCHANGE = "process.queue.exchange";
    
    /**
     * 设置处理队列
     * @return
     */
    @Bean 
    public Queue delayProcess() { 
        return QueueBuilder
                .durable(PROCESS_QUEUE) 
                .build(); 
    }
    
    /**
     * 配置处理交换器
     * @return
     */
    @Bean 
    DirectExchange processExchange() { 
        return new DirectExchange(PROCESS_EXCHANGE); 
    }
    
    /**
     * 将DLX绑定到实际消费队列
     * @return
     */
    @Bean 
    Binding processBinding() {
        return BindingBuilder
                .bind(delayProcess()) 
                .to(processExchange()) 
                .with(PROCESS_QUEUE); 
    }

配置生产者

目前我们创建了两个队列delay.queueprocess.queue(死信队列),这个时候我们的生产者需要向
delay.queue中推送消息,而不是死信队列。死信队列的消息应该是消息在delay.queue中超时后转发到达的。

@Component
@Slf4j
public class DelaySender {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void send(User user) {
        log.info("消息已经发送,时间为:{}",
                new Timestamp(System.currentTimeMillis()));
        this.rabbitTemplate.convertAndSend(
                DelayConfig.PROCESS_EXCHANGE,
                // routingKey
                DelayConfig.PROCESS_QUEUE,
                user);
    }
}

配置消费者

和之前的内容不同,这里使用SimpleMessageListenerContainer完成对队列的监听。
和上面一样,这里我们监听的对象是process.queue队列。

@Component
@Slf4j
public class DelayReceiver {

    /**
     * queues是指要监听的队列的名字
     * @param user
     */
    @RabbitListener(queues = DelayConfig.PROCESS_QUEUE)
    @RabbitHandler
    public void receiveDirect1(User user) {
        log.info("消息已经接收,时间为:{}",new Timestamp(System.currentTimeMillis()));
        System.out.println("【converter-receiveDirect1监听到消息】" + user);
    }

}

测试

我们请求:http://localhost:8000/delay/send 接口推送了一条消息到rabbitMQ中。

后续可以看到控制台打印内容:

2019-08-14 19:52:54.956  INFO 80456 --- [nio-8000-exec-1] dai.samples.rabbit.delay.DelaySender     : 消息已经发送,时间为:2019-08-14 19:52:54.956
2019-08-14 19:52:59.158  INFO 80456 --- [cTaskExecutor-1] dai.samples.rabbit.delay.DelayReceiver   : 消息已经接收,时间为:2019-08-14 19:52:59.158
【converter-receiveDirect1监听到消息】User(id=1, name=Direct, age=100)
为消息设置超时

之前介绍过,超时的设置可以针对队列设置同样也支持对消息进行单独设置。对消息进行单独设置的时候我们消息生产者发起请求需要添加额外的参数。

    public void send2(User user,Long time) {
        log.info("消息已经发送,时间为:{}",new Timestamp(System.currentTimeMillis()));
        this.rabbitTemplate.convertAndSend(
                DelayConfig.DELAY_EXCHANGE,
                // routingKey
                DelayConfig.DELAY_QUEUE,
                user,
                message -> {
                    // 设置延迟毫秒值
                    message.getMessageProperties().setExpiration(String.valueOf(time));
                    return message;
                });
    }

但是需要注意的,当对队列和消息都设置的超时时间的时候,会选择最小的值作为实际超时时间。

测试

消息超时时间大于队列超时时间

我们请求:http://localhost:8000/delay/send/3000 接口推送了一条三秒消息到rabbitMQ中。

后续可以看到控制台打印内容:

2019-08-14 19:55:10.101  INFO 80456 --- [nio-8000-exec-5] dai.samples.rabbit.delay.DelaySender     : 消息已经发送,时间为:2019-08-14 19:55:10.101
2019-08-14 19:55:13.130  INFO 80456 --- [cTaskExecutor-1] dai.samples.rabbit.delay.DelayReceiver   : 消息已经接收,时间为:2019-08-14 19:55:13.13
【converter-receiveDirect1监听到消息】User(id=2, name=Direct2, age=200)

可以看到消息根据我们设置的时间推送到了死信路由


消息超时时间小于队列超时时间

我们请求:delay/send/6000 接口推送了一条消息到rabbitMQ中。

2019-08-14 14:56:04.386  INFO 80456 --- [nio-8000-exec-7] dai.samples.rabbit.delay.DelaySender     : 消息已经发送,时间为:2019-08-14 14:56:04.386
2019-08-14 14:56:08.412  INFO 80456 --- [cTaskExecutor-1] dai.samples.rabbit.delay.DelayReceiver   : 消息已经接收,时间为:2019-08-14 14:56:08.412
【converter-receiveDirect1监听到消息】User(id=2, name=Direct2, age=200)

这个时候可以看到消息并没有根据我们设置时间的6秒钟延迟而是使用了队列的4秒延迟


使用消息超时时候需要注意

现在我们推送一条4秒延迟的消息,再推送一条2秒延迟的消息,按照延迟时间设计应该是我们后续推送的消息最先超时然后被我们消费掉,但实际却不是的。

我们请求:http://localhost:8000/delay/send

然后请求:http://localhost:8000/delay/send/2000

2019-08-14 19:58:53.482  INFO 80456 --- [io-8000-exec-10] dai.samples.rabbit.delay.DelaySender     : 消息已经发送,时间为:2019-08-14 19:58:53.482
2019-08-14 19:58:54.758  INFO 80456 --- [nio-8000-exec-1] dai.samples.rabbit.delay.DelaySender     : 消息已经发送,时间为:2019-08-14 19:58:54.758
2019-08-14 19:58:57.510  INFO 80456 --- [cTaskExecutor-1] dai.samples.rabbit.delay.DelayReceiver   : 消息已经接收,时间为:2019-08-14 19:58:57.51
【converter-receiveDirect1监听到消息】User(id=1, name=Direct, age=100)
2019-08-14 19:58:57.511  INFO 80456 --- [cTaskExecutor-1] dai.samples.rabbit.delay.DelayReceiver   : 消息已经接收,时间为:2019-08-14 19:58:57.511
【converter-receiveDirect1监听到消息】User(id=2, name=Direct2, age=200)

而实际确实第二条消息等待第一条被消费后才被我们消费,实际上延迟超过2秒。

这是因为,由于队列的先进先出特性,只有当过期的消息到了队列的顶端(队首),才会被真正的丢弃或者进入死信队列。
所以当我们设置了一个较短的消息超时时间,但是因为他之前有队列尚未完结。此消息依旧不会进入死信。

消息重试

使用延时消息我们还可以实现一种业务场景:延迟的消息重试。
有些时候我们可能以为一些原因,导致消息在某一时段处理的失败,但是假如马上进行重试可能会再次失败,我们希望稍后对其进行处理且设置一个可控的重试次数。此时我们可以在延迟消息中进行修改来实现消息重试。

重试的流程

成功
重试过多
失败
延时重试
消息
workQueue
处理逻辑
结束
failedQueue
人工处理
retryQueue

根据上面的设计,我们需要三个消息队列workQueue业务队列、retryQueue重试队列、failedQueue失败队列。

配置exchange和queue

workQueue

业务处理所订阅的内容,普通队列

@Configuration
public class WorkConfig {

    /**
     * 处理业务的队列
     */
    public final static String WORK_QUEUE = "retry.work.queue";

    /**
     * 处理业务的交换器
     */
    public final static String WORK_EXCHANGE = "retry.work.exchange";

    /**
     * 处理业务的路由key
     */
    public final static String WORK_KEY = "retry.work.key";


    /**
     * 处理业务的交换器
     * @return
     */
    @Bean 
    DirectExchange retryWorkExchange() {
        return new DirectExchange(WORK_EXCHANGE);
    }


    /**
     * 处理业务的队列
     * @return
     */
    @Bean
    public Queue retryWorkQueue() {
        return QueueBuilder
                .durable(WORK_QUEUE)
                .build();
    }



    /**
     * 绑定处理队列的数据监听工作
     * @return
     */
    @Bean
    public Binding workRetryBinding() {
        return BindingBuilder
                .bind(retryWorkQueue())
                .to(retryWorkExchange())
                .with(WORK_KEY);
    }

}

retryQueue

延时队列,其配置了死信相关参数,其死信队列为workqueue

@Configuration
public class RetryConfig {


    /**
     * 重试的队列
     */
    public final static String RETRY_QUEUE = "retry.queue";

    /**
     * 重试的交换器
     */
    public final static String RETRY_EXCHANGE = "retry.exchange";

    /**
     * 处理业务的路由key
     */
    public final static String RETRY_KEY = "retry.key";

    /**
     * 超时时间
     */
    private static final Long QUEUE_EXPIRATION = 4000L;


   
    /**
     * 重试的交换器
     * @return
     */
    @Bean
    DirectExchange retryExchange() {
        return new DirectExchange(RETRY_EXCHANGE);
    }


    /**
     * 重试的队列
     * @return
     */
    @Bean
    public Queue retryQueue() {
        // 设置超时队列
        return QueueBuilder.durable(RETRY_QUEUE)
                // DLX,dead letter发送到的exchange ,设置死信队列交换器到处理交换器
                .withArgument("x-dead-letter-exchange", WorkConfig.WORK_EXCHANGE)
                // dead letter携带的routing key,配置处理队列的路由key
                .withArgument("x-dead-letter-routing-key", WorkConfig.WORK_KEY)
                // 设置过期时间
                .withArgument("x-message-ttl", QUEUE_EXPIRATION)
                .build();
    }


    /**
     * 绑定处理队列的数据监听工作
     * @return
     */
    @Bean
    public Binding retryBinding() {
        return BindingBuilder
                .bind(retryQueue())
                .to(retryExchange())
                .with(RETRY_KEY);
    }

}

failedQueue

重试次数超过上限后的消息处理队列,没有额外处理

@Configuration
public class FailedConfig {

    /**
     * 处理业务的队列
     */
    public final static String FAILED_QUEUE = "retry.failed.queue";

    /**
     * 处理业务的交换器
     */
    public final static String FAILED_EXCHANGE = "retry.failed.exchange";

    /**
     * 处理业务的路由key
     */
    public final static String FAILED_KEY = "retry.failed.key";


    /**
     * 处理业务的交换器
     * @return
     */
    @Bean DirectExchange retryFailedExchange() {
        return new DirectExchange(FAILED_EXCHANGE);
    }


    /**
     * 处理业务的队列
     * @return
     */
    @Bean
    public Queue retryFailedQueue() {
        return QueueBuilder
                .durable(FAILED_QUEUE)
                .build();
    }



    /**
     * 绑定处理队列的数据监听工作
     * @return
     */
    @Bean
    public Binding failedRetryBinding() {
        return BindingBuilder
                .bind(retryFailedQueue())
                .to(retryFailedExchange())
                .with(FAILED_KEY);
    }
}

配置消息发送者

@Component
public class RetrySender {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void send(User user) {
        this.rabbitTemplate.convertAndSend(
                WorkConfig.WORK_EXCHANGE,
                // routingKey
                WorkConfig.WORK_KEY,
                user);
    }
}

配置消息监听者

在对work_queue的消息订阅中,模拟了业务逻辑,进行重试或者转发至失败队列

@Component
@Slf4j
public class WorkReceiver {

    @Autowired RabbitTemplate rabbitTemplate;
    
    /**
     * queues是指要监听的队列的名字
     * @param user
     */
    @RabbitListener(queues = WorkConfig.WORK_QUEUE,
            errorHandler = "retryReceiverListenerErrorHandler")
    public void receiveDirect(User user,
                              Channel channel,
                              Message message) throws Exception {
        try {
            log.info("【WorkReceiver监听到消息】" + JSON.toJSONString(user));
            Integer retry = user.getRetry();
            String id = user.getId();
            log.info("重试次数:{}",retry);
            if (retry < 3 || "1".equals(id)) {
                user.setRetry(retry + 1);
                throw new RuntimeException("进入重试");
            }
            log.info("消费成功");
        } catch (Exception e) {
            log.info("开始重试");
            if (user.getRetry() > 3) {
                rabbitTemplate.convertAndSend(
                        FailedConfig.FAILED_EXCHANGE,
                        // routingKey
                        FailedConfig.FAILED_KEY,
                        user);
                log.info("receiver failed");
            } else {
                rabbitTemplate.convertAndSend(
                        RetryConfig.RETRY_EXCHANGE,
                        // routingKey
                        RetryConfig.RETRY_KEY,
                        user);
                log.info("receiver error");
            }
        }
    }

}

失败队列的订阅处理中一般会进行数据的持久化,以方便后续人工介入进行业务处理

@Component
@Slf4j
public class FailedReceiver {

    /**
     * queues是指要监听的队列的名字
     * @param user
     */
    @RabbitListener(queues = FailedConfig.FAILED_QUEUE,
            errorHandler = "retryReceiverListenerErrorHandler")
    public void receiveDirect(User user, Channel channel, Message message) throws Exception {
        try {
            log.info("【FailedReceiver监听到消息】" + JSON.toJSONString(user));
            log.info(" 人工处理");
        } catch (Exception e) {
            log.info("receiver error");
        }
    }
}

测试

为了测试编写了两个接口

@RestController
@RequestMapping("retry")
public class RetryController {

    @Autowired
    private RetrySender sender;

    /**
     * 测试重试3次后完成处理
     * @return
     */
    @RequestMapping(value = "send",method = RequestMethod.GET)
    public String sendMessage() {
        User user = new User("3","Direct3",200,0);
        sender.send(user);
        return JSON.toJSONString(user);
    }

    /**
     * 测试重试3次后转入失败队列中
     * @return
     */
    @RequestMapping(value = "send2",method = RequestMethod.GET)
    public String sendMessage2() {
        User user = new User("1","Direct1",200,0);
        sender.send(user);
        return JSON.toJSONString(user);
    }
}

请求接口:http://localhost:8000/retry/send 可以看到下面结果

2019-08-14 20:51:29.891  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 【WorkReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":0}
2019-08-14 20:51:29.891  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 重试次数:0
2019-08-14 20:51:29.891  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 开始重试
2019-08-14 20:51:29.892  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : receiver error
2019-08-14 20:51:33.916  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 【WorkReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":1}
2019-08-14 20:51:33.917  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 重试次数:1
2019-08-14 20:51:33.917  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 开始重试
2019-08-14 20:51:33.919  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : receiver error
2019-08-14 20:51:37.945  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 【WorkReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":2}
2019-08-14 20:51:37.946  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 重试次数:2
2019-08-14 20:51:37.946  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 开始重试
2019-08-14 20:51:37.947  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : receiver error
2019-08-14 20:51:41.974  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 【WorkReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":3}
2019-08-14 20:51:41.975  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 重试次数:3
2019-08-14 20:51:41.975  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 开始重试
2019-08-14 20:51:41.976  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : receiver failed
2019-08-14 20:51:41.999  INFO 89656 --- [cTaskExecutor-1] d.s.r.retry.receiver.FailedReceiver      : 【FailedReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":4}
2019-08-14 20:51:42.000  INFO 89656 --- [cTaskExecutor-1] d.s.r.retry.receiver.FailedReceiver      :  人工处理

请求接口:http://localhost:8000/retry/send2 可以看到下面结果

2019-08-14 20:53:05.751  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 【WorkReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":0}
2019-08-14 20:53:05.751  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 重试次数:0
2019-08-14 20:53:05.752  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 开始重试
2019-08-14 20:53:05.752  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : receiver error
2019-08-14 20:53:09.779  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 【WorkReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":1}
2019-08-14 20:53:09.779  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 重试次数:1
2019-08-14 20:53:09.779  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 开始重试
2019-08-14 20:53:09.780  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : receiver error
2019-08-14 20:53:13.804  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 【WorkReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":2}
2019-08-14 20:53:13.804  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 重试次数:2
2019-08-14 20:53:13.804  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 开始重试
2019-08-14 20:53:13.806  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : receiver error
2019-08-14 20:53:17.827  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 【WorkReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":3}
2019-08-14 20:53:17.827  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 重试次数:3
2019-08-14 20:53:17.827  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : 开始重试
2019-08-14 20:53:17.827  INFO 89656 --- [cTaskExecutor-1] d.s.rabbit.retry.receiver.WorkReceiver   : receiver failed
2019-08-14 20:53:17.853  INFO 89656 --- [cTaskExecutor-1] d.s.r.retry.receiver.FailedReceiver      : 【FailedReceiver监听到消息】{"age":200,"id":"1","name":"Direct1","retry":4}
2019-08-14 20:53:17.854  INFO 89656 --- [cTaskExecutor-1] d.s.r.retry.receiver.FailedReceiver      :  人工处理


本篇文章涉及的源码下载地址:https://gitee.com/daifyutils/springboot-samples

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
1. 添加rabbitmq依赖 在pom.xml中添加以下依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 2. 配置rabbitmq连接信息 在application.properties中添加以下配置: ``` spring.rabbitmq.host=127.0.0.1 spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest ``` 3. 创建消息队列 在代码中创建消息队列,可以使用@Bean注解进行创建: ``` @Bean public Queue delayQueue() { return new Queue("delay_queue"); } ``` 4. 创建交换机 创建交换机,可以使用@Bean注解进行创建: ``` @Bean public DirectExchange delayExchange() { return new DirectExchange("delay_exchange"); } ``` 5. 绑定队列和交换机 将队列和交换机进行绑定,可以使用@Bean注解进行创建: ``` @Bean public Binding delayBinding() { return BindingBuilder.bind(delayQueue()).to(delayExchange()).with("delay_key"); } ``` 6. 设置延时消息的TTL 设置延时消息的TTL,可以使用@Bean注解进行创建: ``` @Bean public CustomExchange customExchange() { Map<String, Object> args = new HashMap<>(); args.put("x-delayed-type", "direct"); return new CustomExchange("delayed_exchange", "x-delayed-message", true, false, args); } ``` 7. 发送延时消息 发送延时消息,可以使用rabbitTemplate发送消息: ``` rabbitTemplate.convertAndSend("delayed_exchange", "delay_key", message, message1 -> { message1.getMessageProperties().setHeader("x-delay", delayTime); return message1; }); ``` 其中,delayTime为延时时间,单位为毫秒。 完整代码如下: ``` @Configuration public class RabbitmqConfig { @Autowired private RabbitTemplate rabbitTemplate; @Bean public Queue delayQueue() { return new Queue("delay_queue"); } @Bean public DirectExchange delayExchange() { return new DirectExchange("delay_exchange"); } @Bean public Binding delayBinding() { return BindingBuilder.bind(delayQueue()).to(delayExchange()).with("delay_key"); } @Bean public CustomExchange customExchange() { Map<String, Object> args = new HashMap<>(); args.put("x-delayed-type", "direct"); return new CustomExchange("delayed_exchange", "x-delayed-message", true, false, args); } public void sendDelayMessage(Object message, Integer delayTime) { rabbitTemplate.convertAndSend("delayed_exchange", "delay_key", message, message1 -> { message1.getMessageProperties().setHeader("x-delay", delayTime); return message1; }); } } ``` 调用sendDelayMessage方法即可发送延时消息

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值