以前的项目实现延时功能,基于redis的过期机制 ,但是redis的弊端过于明显,后来改为rabbitmq的死信队列,今天有空改为了基于延迟插件。简单记录一下。
延迟插件的安装
rabbitmq的安装这里不再赘述,根据自己安装的Rabbitmq版本选择对应的延迟插件。https://rabbitmq.com/community-plugins.html
然后根据自己mq版本选择对应的插件
然后把插件放到安装目录的plugins下面,执行
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
项目集成mq
简单的依赖引入 这里不再赘述,开启手动确认 确保准确被消费。
spring:
rabbitmq:
host: 服务器ip
username: guest
password: guest
port: 5672
virtual-host: /
#开启确认机制
publisher-confirm-type: correlated
publisher-returns: true
listener:
simple:
acknowledge-mode: manual
@Configuration
public class RabbitMqConfig {
private final static Logger log = LoggerFactory.getLogger(RabbitMqConfig.class);
//延迟交换机
public static final String DELAY_EXCHANGE = "delay_exchange";
//延迟队列
public static final String DELAY_QUEUE = "delay_queue";
//延迟路由键
public static final String DELAY_QUEUE_ROUTING_KEY = "delay_queue_routing_key";
@Autowired
private CachingConnectionFactory cachingConnectionFactory;
//队列
@Bean
public Queue delyqueue() {
return new Queue(DELAY_QUEUE, true);
}
//交换机
@Bean
public DirectExchange delyExchange() {
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-message", "direct");
DirectExchange exchange = new DirectExchange(DELAY_EXCHANGE, true, false, args);
exchange.setDelayed(true);
return exchange;
}
//绑定
@Bean
public Binding delyBind() {
return BindingBuilder.bind(delyqueue()).to(delyExchange()).with(DELAY_QUEUE_ROUTING_KEY);
}
@Bean
RabbitTemplate rabbitTemplate() {
RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
//发送情况监控
rabbitTemplate.setConfirmCallback((data, ack, cause) -> {
String msgId = data.getId();
System.out.println(data.toString());
if (ack) {
log.info("message发送成功" + msgId);
} else {
log.info("message发送失败" + msgId);
}
});
//rabbit自身错误监控
rabbitTemplate.setReturnCallback((msg, repCode, repText, exchange, routingkey) -> {
log.info("Rabblimq自身原因邮件发送失败" + msg);
});
return rabbitTemplate;
}
}
生产者,这里修改为把传递的标识放到自定义的header里面。自带提供的获取的时候会遇到为null的情况。
@Service
public class producerService {
private final static Logger logger = LoggerFactory.getLogger(producerService.class);
@Autowired
private RabbitTemplate rabbitTemplate;
public void sentDelyMsg(Integer delayTime, String message) {
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
logger.info("发送信息的标识为:" + uuid);
logger.info("-------------开启延迟队列-------------");
rabbitTemplate.convertAndSend("delay_exchange", "delay_queue_routing_key",
message, msg -> {
//setDelay()的本质是对消息头设置 x-delay 参数,用来指定消息的延迟时间
msg.getMessageProperties().setDelay(delayTime);
//把CorrelationData里面的id放到MessageProperties里面
msg.getMessageProperties().setHeader("uuid", uuid);
return msg;
}, new CorrelationData(uuid));
logger.info("-------------延迟队列任务建立-------------");
}
}
消费者,获取唯一标识的时候从header里面取即可
@Component
public class Receive {
private final static Logger logger = LoggerFactory.getLogger(Receive.class);
@RabbitListener(queues = "delay_queue")
public void receiveMessage(Message message, Channel channel) throws Exception {
long tag = message.getMessageProperties().getDeliveryTag();
Object msgId = message.getMessageProperties().getHeaders().get("uuid");
String msg = new String(message.getBody());
logger.info("当前时间:{},从延迟队列中消费的消息:{}", LocalDateTime.now(), msg);
try {
//模拟出现异常
int i = 5 / 0;
channel.basicAck(tag, false);
logger.info(msgId + ":消费成功");
} catch (Exception e) {
//拒绝签收
//channel.basicNack(tag, false, true);
logger.error(e.getMessage(), e);
//然后把错误的信息 单独记录到错误的信息表里面
} finally {
channel.basicAck(tag, false);
logger.info(msgId + ":消费成功");
}
}
}
结果展示