RabbitMq和RocketMq如何实现消息延迟发送

RabbitMq实现消息延迟发送的两种方式

方式一:TTL+死信:间接实现了延迟消息

原理

生产者发送一个带有过期时间的消息,通过交换机转发给没有消费者的队列,消息在队列中存储,过期时间到了之后,
会转发给死信交换机,死信交换机再通过配置的死信的路由key转发给死信队列,队列再转发给消费者,这样就完成了一个消息的延迟转发

缺点:

如果使用这种方法如果传输的数据过期时间不一致时会出现数据传输的偏差(只适用于过期时间相同的数据)
如果过期时间短的排在过期时间长的后面,会导致已经过期的数据无法发送给队列,需要等到队列前的数据过期后才能发送给队列

image

生产者

     /*
        死信队列发送消息
     */
    @Test
    public void testDeadLetter(){
        Message msg = MessageBuilder.withBody("hello,我是通过死信队列发送的消息".getBytes(StandardCharsets.UTF_8))
                .setExpiration("1500")//给消息设置过期时间,消息过期后会扔到死信交换机中
                .build();
        template.convertAndSend("directEx","ttlKey",msg);
    }

消费者配置类

@Configuration
public class TTLConfig {
    //创建一个direct普通交换机
    @Bean
    public DirectExchange getDirectExchange(){
        return new DirectExchange("directEx");
    }

    //将普通队列与direct交换机绑定
    @Bean
    public Binding getBind1(){
        return BindingBuilder.bind(getQueue()).to(getDirectExchange()).with("ttlKey");
    }


    //创建一个普通队列
    @Bean
    public Queue getQueue(){
        return QueueBuilder.durable("ttlQueue")//创建一个普通队列
                .deadLetterExchange("deadLetterEx")//绑定死信交换机
                .ttl(800)//创建队列过期时间
                .deadLetterRoutingKey("deadRoutingKey")//创建死信的路由key
                .build();// 构建
    }
}

消费者监听端口

@Component
public class DeadLetterListener {
    //将死信交换机死信队列绑定
    @RabbitListener(bindings = {@QueueBinding(value = @Queue(name = "deadLetterQueue"),
            exchange = @Exchange(name = "deadLetterEx" ), key = "deadRoutingKey")})
    public void DeadLetter(String msg) {
        System.err.println("接收到了死信交换机的消息 " + msg);
    }
}

方式二:在RabbitMQ中安装一个延迟的插件

在服务器上安装延迟插件

cd /opt/   # 切换到安装插件的目录

docker cp /opt/rabbitmq_delayed_message_exchange-3.8.0.ez rabbitmq:/plugins  # 将插件copy到docker中

cd plugins

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

exit

docker restart rabbitmq

生产者

 @Test
    public void testDelayPlugins(){
        String msg = "延迟插件完成";
        template.convertAndSend("delay_exchange", "delay.key", msg, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //"x-delay"是是头的属性名,是固定的,告诉插件延迟时间是多少
                message.getMessageProperties().setHeader("x-delay",8000);
                return message;
            }
        });
    }

消费者配置类

@Configuration
public class TTLDelayConfig {

    //创建一个自定义交换机
    @Bean
    public CustomExchange customExchange(){
        Map<String, Object> args = new HashMap<>(1);
        args.put("x-delayed-type","direct");
        return new CustomExchange("delay_exchange","x-delayed-message",true,false,args);
    }

    //创建一个队列
    @Bean
    public Queue getDelayQueue(){
        return new Queue("delay_queue");
    }

    //绑定交换机队列
    @Bean
    public Binding getBingding(){
        return BindingBuilder.bind(getDelayQueue()).to(customExchange()).with("delay.key").noargs();
    }


}

消费者监听端口

@Component
public class DelayPluginsListener {
    @RabbitListener(queues = "delay_queue")
    public void getDelayPluginsMsg(String msg){
        System.out.println("msg = " + msg);
    }
}

Rocketmq延迟消息实现

Rocketmq相对于Rabbitmq的延迟消息会简单很多,可以直接设置消息延迟到达的时间,但是开源的只支持以下18个延迟级别
 目前支持的消息时间

◆ 秒级:1,5,10,30
◆ 分级:1~10,20,30
◆ 时级:1,2
◆ 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

Message msg = new Message("topic3",("延时消息:hello rocketmq "+i).getBytes("UTF-8"));
//设置当前消息的延时效果
msg.setDelayTimeLevel(3);
SendResult result = producer.send(msg);
System.out.println("返回结果:"+result);
posted @ 2023-03-26 17:10  奕生呀  阅读(366)  评论(0)    收藏  举报