pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
yml
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
virtual-host: /api/mall #mq设置的路由
username: root
password: password
publisher-confirms: true #消息发送到交换器确认
publisher-returns: true #消息发送到队列确认
listener:
simple:
#消费失败消息干掉
default-requeue-rejected: true
retry:
#5秒
initial-interval: 5000
enabled: true
#最大重试5次
max-attempts: 5
config配置
RabbitMQ.java
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.dandandog.framework.common.utils.SpringContextUtil;
import com.dandandog.framework.mq.model.ImqBinding;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.CustomExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Stephen
*/
@Configuration
public class RabbitMqConfig implements InitializingBean {
private final List<ImqBinding> bindings;
@Autowired
public RabbitMqConfig(List<ImqBinding> bindings) {
this.bindings = bindings;
}
@Override
public void afterPropertiesSet() {
if (CollUtil.isNotEmpty(bindings)) {
Multimap<String, ImqBinding> bindingMap = ArrayListMultimap.create();
bindings.forEach(binding -> {
bindingMap.put(binding.getExchange(), binding);
});
bindingMap.keySet().forEach(exchangeName -> {
CustomExchange customExchange = buildCustomExchange(exchangeName);
bindingMap.get(exchangeName).forEach(binding -> {
Queue queue = buildPluginQueue(binding.getQueue());
buildPluginBinding(customExchange, queue, binding.getRouteKey());
});
});
}
}
private CustomExchange buildCustomExchange(String exchangeName) {
Map<String, Object> args = new HashMap<>(1);
args.put("x-delayed-type", "direct");
String beanName = createBeanName(exchangeName, "Exchange");
return SpringContextUtil.registerBean(beanName, CustomExchange.class,
() -> new CustomExchange(exchangeName, "x-delayed-message", true, false, args));
}
private Queue buildPluginQueue(String queueName) {
String beanName = createBeanName(queueName, "Queue");
return SpringContextUtil.registerBean(beanName, Queue.class,
() -> new Queue(queueName, true));
}
private Binding buildPluginBinding(CustomExchange exchange, Queue queue, String routeKey) {
String beanName = createBeanName(routeKey, "Binding");
return SpringContextUtil.registerBean(beanName, Binding.class,
() -> BindingBuilder.bind(queue).to(exchange).with(routeKey).noargs());
}
private String createBeanName(String name, String suffix) {
String repName = StrUtil.replace(name, ".", "_");
String suffixName = StrUtil.addSuffixIfNot(repName, suffix);
return StrUtil.toCamelCase(suffixName);
}
}
交换机绑定接口
/**
* @author Stephen
*/
public interface ImqBinding {
/**
* 获取交换机名称
*
* @return 交换机名称
*/
String getExchange();
/**
* 获取队列名称
*
* @return 队列名称
*/
String getQueue();
/**
* 获取路由键
*
* @return 路由键
*/
String getRouteKey();
}
消息发送抽象类
import com.dandandog.framework.mq.model.ImqBinding;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.LocalDateTime;
/**
* @author Stephen
*/
@Component
public abstract class BaseDelaySender<T extends ImqBinding> {
private final AmqpTemplate amqpTemplate;
private final T binding;
@Autowired
public BaseDelaySender(AmqpTemplate amqpTemplate, T binding) {
this.amqpTemplate = amqpTemplate;
this.binding = binding;
}
public void sendDelayMessage(Object data, long delayTimes) {
amqpTemplate.convertAndSend(binding.getExchange(), binding.getRouteKey(), data, message -> {
message.getMessageProperties().setHeader("x-delay", delayTimes);
return message;
});
}
public void sendDelayMessage(Object data, LocalDateTime delayDataTime) {
Duration delayTimes = Duration.between(LocalDateTime.now(), delayDataTime);
this.sendDelayMessage(data, delayTimes.toMillis());
}
}
业务使用,例如订单付款失败30分钟后取消订单
设置交换机与队列名称
import com.dandandog.framework.mq.model.ImqBinding;
import org.springframework.stereotype.Service;
/**
* @author StephenZhang
*/
@Service
public class OrderCancelMessageBinding implements ImqBinding {
@Override
public String getExchange() {
return "message.direct.plugin";
}
@Override
public String getQueue() {
return "mall.order.cancel.plugin";
}
@Override
public String getRouteKey() {
return "mall.order.cancel.plugin";
}
}
消息发送实现类
import com.dandandog.framework.mq.sender.BaseDelaySender;
import com.myelephant.projects.rabbit.config.OrderCancelMessageBinding;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @author Stephen
*/
@Component
public class MallCancelOrderSender extends BaseDelaySender<OrderCancelMessageBinding> {
@Autowired
public MallCancelOrderSender(AmqpTemplate amqpTemplate, OrderCancelMessageBinding binding) {
super(amqpTemplate, binding);
}
}
消息接收实现类
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.myelephant.module.integral.entity.enums.UserLogType;
import com.myelephant.module.integral.service.IntegralUserLogService;
import com.myelephant.module.mall.entity.MallOrder;
import com.myelephant.module.mall.entity.enums.OrderState;
import com.myelephant.module.mall.service.MallOrderService;
import com.myelephant.projects.integral.facet.IntegralTaskFacet;
import com.myelephant.projects.mall.give.facet.GiveProductOrderFacet;
import com.myelephant.projects.mall.give.vo.OrderCancelMqVo;
import jodd.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* @author StephenZhang
*/
@Component
@RabbitListener(queues = "mall.order.cancel.plugin")
public class MallCancelOrderReceiver {
private static final Logger LOGGER = LoggerFactory.getLogger(MallCancelOrderReceiver.class);
@Resource
private MallOrderService mallOrderService;
@Resource
private IntegralUserLogService integralUserLogService;
@Resource
private IntegralTaskFacet integralTaskFacet;
@Resource
private GiveProductOrderFacet giveProductOrderFacet;
@Resource
private RedisTemplate<String, List<MallOrder>> redisTemplate;
@Value("${spring.redis.key.prefix.mallOrderSpellGift}")
private String orderSpellGiftKey;
@RabbitHandler
public synchronized void handle(String params) {
//具体业务实现
LOGGER.info("receive delay message params:{}", params);
}
}
方法调用
@Resource
private MallCancelOrderSender mallcancelOrderSender;
可指定毫秒或者指定时间
mallcancelOrderSender.sendDelayMessage(params, (30 * 60 * 1000));
Linux下安装MQ+延迟插件
https://blog.csdn.net/Dong_Zi8/article/details/116021223?spm=1001.2014.3001.5501