@Slf4j
public abstract class DelayedRetryQueue {
private final static String DELAY_MARKING = ".delay";
private final static String EXCHANGE_MARKING = ".direct-exchange";
private final static String DELAY_COUNT = "delay-count";
private final String queue;
private final String exchange;
private final String delayQueue;
private final String delayExchange;
private final RabbitTemplate rabbitTemplate;
private boolean retry = true;
public DelayedRetryQueue(RabbitTemplate rabbitTemplate, String queue) throws Exception {
this.rabbitTemplate = rabbitTemplate;
this.queue = queue;
this.exchange = this.queue + EXCHANGE_MARKING;
this.delayQueue = this.queue + DELAY_MARKING;
this.delayExchange = this.exchange + DELAY_MARKING;
init();
}
public DelayedRetryQueue(RabbitTemplate rabbitTemplate, String queue, boolean retry) throws Exception {
this(rabbitTemplate, queue);
this.retry = retry;
}
public void init() throws IOException, TimeoutException {
ConnectionFactory connectionFactory = rabbitTemplate.getConnectionFactory();
Connection connection = connectionFactory.createConnection();
Channel channel = connection.createChannel(false);
log.info("create consumption queue of {}", queue);
channel.queueDeclare(queue, true, false, false, null);
channel.exchangeDeclare(exchange, "direct", true);
channel.queueBind(queue, exchange, exchange);
log.info("create delay queue of {}", queue);
Map<String, Object> args = new HashMap<>(2);
args.put("x-dead-letter-exchange", exchange);
args.put("x-dead-letter-routing-key", exchange);
channel.queueDeclare(delayQueue, true, false, false, args);
channel.exchangeDeclare(delayExchange, "direct", true);
channel.queueBind(delayQueue, delayExchange, delayExchange);
channel.close();
}
protected abstract void consume(Message message, Channel channel) throws Exception;
protected abstract void consumeFail(Message message, Throwable throwable);
protected abstract void discard(Message message, Throwable throwable);
protected void instantProduce(byte[] bytes) {
Message message = new Message(bytes, new MessageProperties());
rabbitTemplate.send(exchange, exchange, message);
}
protected void delayProduce(byte[] bytes, long time) {
Message message = new Message(bytes, new MessageProperties());
MessageProperties messageProperties = message.getMessageProperties();
messageProperties.setExpiration(String.valueOf(time));
rabbitTemplate.send(delayExchange, delayExchange, message);
}
@RabbitHandler(isDefault = true)
public void rabbitListenerHandle(Message message, Channel channel) {
try {
consume(message, channel);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (Throwable e) {
consumeFail(message, e);
if (retry) {
int delayCount = (int) Optional.ofNullable(message.getMessageProperties().getHeader(DELAY_COUNT)).orElse(1);
RabbitDelayEnum level = RabbitDelayEnum.getLevel(delayCount);
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
} catch (IOException ex) {
ex.printStackTrace();
}
if (level == null) {
log.error(String.format("queue: %s 消费失败,执行丢弃", queue), e);
discard(message, e);
} else {
log.error(String.format("queue: %s 消费失败,尝试重试", queue), e);
MessageProperties messageProperties = message.getMessageProperties();
messageProperties.setExpiration(level.getTime());
messageProperties.setHeader(DELAY_COUNT, ++delayCount);
rabbitTemplate.send(delayExchange, delayExchange, message);
}
} else {
log.error(String.format("queue: %s 消费失败,执行丢弃", queue), e);
discard(message, e);
}
throw new RuntimeException(e);
}
}
}