RabbitMQ的重试机制,延迟队列,死信队列

一:队列配置
1)失败队列

package com.future.rabbit.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@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);
    }
}

2)重试队列

package com.future.rabbit.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@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 = 3000L;


    /**
     * 重试的交换器
     *
     * @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);
    }

}

3)工作队列

package com.future.rabbit.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@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);
    }

}

二:生产者配置

package com.future.rabbit.handle;

import com.future.rabbit.config.WorkConfig;
import com.future.rabbit.idgenerator.SnowflakeIdWorker;
import com.future.rabbit.pojo.User;
import com.future.rabbit.utils.TimeUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class RetrySender {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private SnowflakeIdWorker worker;

    public void send() {

        this.rabbitTemplate.convertAndSend(
                WorkConfig.WORK_EXCHANGE,
                // routingKey
                WorkConfig.WORK_KEY,
                new User(worker.genNextId(), "Direct2", 200, 0, TimeUtils.getChinaTime()));
    }
}


三:消费者配置
1)正常工作消费

package com.future.rabbit.handle;

import com.future.rabbit.config.FailedConfig;
import com.future.rabbit.config.RetryConfig;
import com.future.rabbit.config.WorkConfig;
import com.future.rabbit.constant.RabbitConstant;
import com.future.rabbit.pojo.User;
import com.future.rabbit.service.impl.SysUserServiceImpl;
import com.future.rabbit.utils.GsonUtil;
import com.rabbitmq.client.Channel;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@AllArgsConstructor
public class WorkReceiver {


    private final RabbitTemplate rabbitTemplate;
    private final SysUserServiceImpl sysUserService;

    /**
     * queues是指要监听的队列的名字
     *
     * @param user
     */
    @RabbitListener(queues = WorkConfig.WORK_QUEUE
    ) //errorHandler = "retryReceiverListenerErrorHandler"
    public void receiveDirect(User user,
                              Channel channel,
                              Message message) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            log.info("【WorkReceiver监听到消息】" + GsonUtil.gsonString(user));
            //业务处理,返回flag
            if (flag) {
                channel.basicAck(deliveryTag, false);
            }
        } catch (Exception e) {
            log.error(e.getMessage()+e.getCause());
            //进入延时队列,进行重试
            Integer retry = user.getRetry();
            if (retry < RabbitConstant.QUEUE_RETRY_NUM) {
                log.info("receiver error,进入延迟队列,重试次数:{}", retry + 1);
                user.setRetry(retry + 1);
                rabbitTemplate.convertAndSend(
                        RetryConfig.RETRY_EXCHANGE,
                        // routingKey
                        RetryConfig.RETRY_KEY,
                        user);
            } else {
                //进入失败队列,进行人工处理
                rabbitTemplate.convertAndSend(
                        FailedConfig.FAILED_EXCHANGE,
                        // routingKey
                        FailedConfig.FAILED_KEY,
                        user);
                log.info("receiver failed,进入死信队列");
            }
            //将上一个重试的队列手动ack
            channel.basicAck(deliveryTag, false);
        }
    }
}

2)失败消费

package com.future.rabbit.handle;

import com.future.rabbit.config.FailedConfig;
import com.future.rabbit.pojo.MessageStruct;
import com.future.rabbit.pojo.User;
import com.future.rabbit.service.impl.MessageServiceImpl;
import com.future.rabbit.utils.GsonUtil;
import com.rabbitmq.client.Channel;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@AllArgsConstructor
public class FailedReceiver {

    private final MessageServiceImpl messageService;

    /**
     * queues是指要监听的队列的名字
     *
     * @param user
     */
    @RabbitListener(queues = FailedConfig.FAILED_QUEUE
    ) //  errorHandler = "retryReceiverListenerErrorHandler"
    public void receiveDirect(User user, Channel channel, Message message) throws Exception {
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            String userMessage = GsonUtil.gsonString(user);
            log.info("【FailedReceiver监听到消息,存到数据库】" + userMessage);
            //业务处理,返回flag
            if (flag) {
                //如果失败消息存储成功,则手动ack
                channel.basicAck(deliveryTag, false);
            }
        } catch (Exception e) {
            log.info("receiver error");
        }
    }
}

四:通用常数

package com.future.rabbit.constant;

/**
 * RabbitMQ常量池
 *
 * @author yangkai.shen
 */
public interface RabbitConstant {
    /**
     * 直接模式1
     */
    String DIRECT_MODE_QUEUE_ONE = "queue.direct.1";

    /**
     * 队列2
     */
    String QUEUE_TWO = "queue.2";

    /**
     * 队列3
     */
    String QUEUE_THREE = "3.queue";

    /**
     * 分列模式
     */
    String FANOUT_MODE_QUEUE = "fanout.mode";

    /**
     * 主题模式
     */
    String TOPIC_MODE_QUEUE = "topic.mode";

    /**
     * 路由1
     */
    String TOPIC_ROUTING_KEY_ONE = "queue.#";

    /**
     * 路由2
     */
    String TOPIC_ROUTING_KEY_TWO = "*.queue";

    /**
     * 路由3
     */
    String TOPIC_ROUTING_KEY_THREE = "3.queue";

    /**
     * 延迟队列
     */
    String DELAY_QUEUE = "delay.queue";

    /**
     * 延迟队列交换器
     */
    String DELAY_MODE_QUEUE = "delay.mode";

    /**
     * 重试次数
     */
    int QUEUE_RETRY_NUM = 3;

    /**
     * 雪花id机器id
     */
    int WORKER_ID = 30;

    /**
     * 雪花id数据id
     */
    int DATA_ID = 14;


}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值