rabbitmq延时队列
rabbitmq延时队列
在开发中使用到定时任务这个功能,以前都是使用timer,quartz,redis的过期事件,quartz的话做集群依赖数据库,redis使用集群的时候不支持过期事件,都存在各种问题,所有想到了使用rabbitmq的延时队列来实现,那这其中也涉及到rabbitmq事务的相关设置,这里就简单写一下我工作中的体会
定义延时队列
这里就不再详细的介绍基本概念,主要涉及 发布者,交换器,队列,消费者,消费者和队列之间通过TCP建立Connection,connection中存在channel
下面为延时队列的一个流程图,
下面直接贴代码:
//创建连接工厂,注意publisherConfirms,这个值必须设置
@Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
//消息发送到rabbitmq交换器后接受ack回调,回调中会告诉发送方,
//rabbit是否接受成功,没有成功的话,要做相应的处理,比如重发或者记录
connectionFactory.setPublisherConfirms(true); //这里相当于通过这个设置能实现事务的一半
//消息发送到rabbitmq交换器后无相应队列育交换器绑定时进行回调
connectionFactory.setPublisherReturns(true);
return connectionFactory;
}
/**
* 声明延迟队列交换器 exchange
* @return
*/
@Bean
public DirectExchange delayExchange() {
return new DirectExchange("delay_exchange");
}
/**
* 声明延迟队列 queue
*/
@Bean
Queue delayQueue(){
return QueueBuilder.durable("delay_queue")
// DLX,dead letter发送到的exchange:消息过期后转到的exchange
.withArgument("x-dead-letter-exchange", "process_exchange")
// dead letter携带的routing key:跳转到指定exchange后传送到指定routingkey的队列
.withArgument("x-dead-letter-routing-key", "delay")
.build();
}
/**
* 将延迟队列与exchange绑定
* @return
*/
@Bean
Binding delayBinding() {
return BindingBuilder.bind(delayQueue())
.to(delayExchange())
.with("delay");
}
上面延迟队列就声明完毕,下面声明一下正常消费队列
//正常消费队列 交换器exchange 注意这里exchangeName和上面x-dead-letter-exchange对应的一致
@Bean
public DirectExchange processExchange() {
return new DirectExchange("process_exchange");
}
@Bean
public Queue processQueue() {
return QueueBuilder.durable("process_queue")
.build();
}
@Bean
Binding queueBinding() {
return BindingBuilder.bind(processQueue())
.to(processExchange())
//这个值必须和x-x-dead-letter-routing-key保持一致,这样才会传输到指定的队列中
.with("delay");
}
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //必须是prototype类型(有待考证)
RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
//使用return-callback时必须设置mandatory为true
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
//做相应的操作
}
});
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String s) {
if (!ack) {
logger.info("=============消息发送失败=============");
Msg msg = JSON
.parseObject(correlationData.getId(), Msg.class);
//这里做了重新传送,Msg是自定义的类,传递的消息。correlationData会设置一个id,在发送的时候设置一下,
//这里由于是返回的,已经带有id不用重新再设置
rabbitTemplate.convertAndSend(exchangeName, forceCloseRepoRouting, msg, correlationData);
} else {
logger.info("=============消息发送成功=============");
}
logger.info("correlationData= {},b={},s={}",correlationData!=null?correlationData.toString():"",ack,s);
}
});
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplate;
}
消息发送方法:
public void send() {
Msg msg=new Msg();
msg.setId(1);
//携带确认数据,设置correlationDate的id,主要是用于消息没有发送成功,返回给生产者是通过id判断是哪个信息
CorrelationData correlationData = new CorrelationData(JSON.toJSONString(msg));
rabbitTemplate.convertAndSend(exchangeName, forceCloseRepoRouting, msg, correlationData);
}
到这里除了监听代码没有写出来,其他的代码都已经写出来了,慢慢干货,当然这里还有一些问题,后面有时间在和大家一块儿学习讨论吧