安装rabbitmq,安装erlang,需要对应好版本,可以看以往文章有百度云分享链接,
下载对应版本rabbitmq的延时插件(如果你的延时需求都是延时同样的时间,那可以试试rabbitmq原本就有的死信队列,来实现)插件去官方github上下载百十k,一切都处理完就可以写代码了。
package com.lianpo.fanfou.system.config;
import com.alibaba.fastjson.JSON;
import com.lianpo.fanfou.api.client.ota.constant.SyncTypeEnum;
import com.lianpo.fanfou.mysql.repository.entity.refund.RefundPreReviewOrderDO;
import com.lianpo.fanfou.mysql.repository.serivce.refund.RefundPreReviewOrderService;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
public class MqConfig implements RabbitTemplate.ConfirmCallback{
public static final String QUEUE_NAME = "ORDER_DELAY";
public static final String EXCHANGE_NAME = "ORDER_EXCHANGE";
public static final String ROUTING_KEY = "ORDER_ROUTING_KEY";
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.port}")
private Integer port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
public static ConnectionFactory connectionFactory(String host, Integer port, String username, String password) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
return connectionFactory;
}
private static RabbitTemplate rabbitTemplate;
@PostConstruct
public void init(){
rabbitTemplate = new RabbitTemplate(connectionFactory(host,port,username,password));
//开启发送失败退回
rabbitTemplate.setMandatory(true);
rabbitTemplate.setConfirmCallback(this);
}
@Bean("listenerFactory")
public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
factory.setConcurrentConsumers(5);
//最大线程数
factory.setMaxConcurrentConsumers(10);
/* setConnectionFactory:设置spring-amqp的ConnectionFactory。 */
factory.setConnectionFactory(connectionFactory);
factory.setPrefetchCount(1);
//factory.setDefaultRequeueRejected(true);
//使用自定义线程池来启动消费者。
factory.setTaskExecutor(taskExecutor());
return factory;
}
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(0);
executor.setKeepAliveSeconds(300);
executor.setThreadNamePrefix("listener");
// 设置拒绝策略rejection-policy:当pool已经达到max size的时候,丢弃
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.initialize();
return executor;
}
//定义延时交换机 -- 插件版本
//指定交换器类型为 x-delayed-message
@Bean("exchange_1")
public CustomExchange orderEventExchange(){
HashMap<String,Object> hashMap = new HashMap<>();
hashMap.put("x-delayed-type","direct");
return new CustomExchange(EXCHANGE_NAME,"x-delayed-message",true,false,hashMap);
//预约审核订单交换机
//第1个参数是名字//第2个参数:交换机的类型//第3个参数是是否持久化
// 第4个参数是表示是否自动删除(没有绑定时自动删除这个交换机)//第5个参数是设置交换机的参数
}
//插件版本 -- 实现延迟队列
@Bean("queue_1")
public Queue relevancyDelayedQueue() {
return new Queue(QUEUE_NAME);
}
/*@Bean
public Queue DelayQueue(){
HashMap<String,Object> map = new HashMap<>();
map.put("x-dead-letter-exchange","order-event-exchange");
map.put("x-dead-letter-routing-key","order.release.order");//死信时候以order.release.order的rotingkey发给order-event-exchange
map.put("x-message-ttl",180000);//消息过期时间是1分钟,进入这个队列的消息一分钟内不被消费就会成为死信,这个是给队列整体设置的超时
//不建议使用消息单独超时,因为队列懒检查的方式解决的,及处理完队列头部才看第二个消息有没有过期,
//不过如果这个队列只放超时消息并且超时时间一样根据入队时间的先后,也完全没影响
return new Queue("order.delay.queue",true,false,false,map);
}*/
/*绑定*/
@Bean
public Binding bindingNotify(@Qualifier("queue_1") Queue relevancyDelayedQueue,
@Qualifier("exchange_1") CustomExchange relevancyDelayedExchange) {
return BindingBuilder
.bind(relevancyDelayedQueue)
.to(relevancyDelayedExchange)
.with(ROUTING_KEY).noargs();
}
// 发消息
public static void send(String message,Long delayTime){
MessagePostProcessor messagePostProcessor = message1 -> {
message1.getMessageProperties().setHeader("x-delay",delayTime);//消息的过期时间//x-delay时间最多只能49天
return message1;
};
rabbitTemplate.convertAndSend(PreReviewMqConfig.EXCHANGE_NAME,PreReviewMqConfig.ROUTING_KEY,message,messagePostProcessor);
}
/*消息发送确认*/
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
String id = correlationData != null ? correlationData.getId() : "";
if (!b) {
Message returnedMessage = correlationData != null ? correlationData.getReturnedMessage():null;
if(returnedMessage != null){
// 发送失败消息处理逻辑
}
// 处理数据库消息为发送失败
}
}
}
Listener 代码示例
@RabbitListener(containerFactory = "listenerFactory", bindings = {
@QueueBinding(value = @Queue(MqConfig.QUEUE_NAME),
exchange = @Exchange(name = MqConfig.EXCHANGE_NAME, type = "x-delayed-message"),
key = PreReviewMqConfig.ROUTING_KEY)
})
public void consumer(String msgContent, Message message, Channel channel) throws IOException { //消息自动确认是指消息发出后就认为消息消费成功,消息就会被RabbitMQ从队列中删除掉
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// try内容替换成你自己的处理逻辑
DO = Service.getById(Integer.valueOf(msgContent));
if () {// 防止重复消费逻辑
return;
}
preReviewRefund.consume(refundPreReviewOrderDO);
} finally {
channel.basicAck(deliveryTag, false); //basicReject的第二个参数表示是否重新入队列,
}
}