<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum QueueEnum {
/**
* 死信队列
*/
QUEUE_ORDER_CANCEL("order_dlx_exchange:", "order_dlx_queue:", "order_dlx_key"),
/**
* 延时队列
*/
QUEUE_TTL_ORDER_CANCEL("order_ttl_exchange:", "order_ttl_queue:", "order_ttl_key"),
/**
* 退款队列
*/
REFUND_QUEUE("refund_exchange", "refund_queue:", "refund_key"),
;
/**
* 交换名称
*/
private final String exchange;
/**
* 队列名称
*/
private final String queueName;
/**
* 路由键
*/
private final String routeKey;
}
- MQ配置类 - 声明Queue和Exchange与相互之间的Binding关系
import cn.hutool.core.map.MapUtil;
import com.demo.common.enums.QueueEnum;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* 消息队列 配置类
*/
@EnableRabbit
@Configuration
public class MqConfig {
/* 订单退款相关 start */
/**
* 退款的消息队列
*/
@Bean
public Queue refundQueue() {
return new Queue(QueueEnum.REFUND_QUEUE.getQueueName());
}
/**
* 退款的exchange
*/
@Bean
public CustomExchange refundExchange() {
Map<String, Object> param = MapUtil.newHashMap();
param.put("x-delayed-type", "direct");
return new CustomExchange(QueueEnum.REFUND_QUEUE.getExchange(), "x-delayed-message", true, false, param);
}
/**
* 绑定退款队列、交换机的关系
*/
@Bean
public Binding binding(
@Qualifier("refundQueue") Queue refundQueue,
@Qualifier("refundExchange") CustomExchange exchange
) {
return BindingBuilder.bind(refundQueue).to(exchange).with(QueueEnum.REFUND_QUEUE.getRouteKey()).noargs();
}
/* 订单退款相关 end */
/* 延时队列 start */
/**
* 订单延迟队列
*/
@Bean
public Queue orderTtlQueue() {
return QueueBuilder
.durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getQueueName())
//到期后转发的交换机
.deadLetterExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange())
//到期后转发的路由键
.deadLetterRoutingKey(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())
.build();
}
/**
* 订单延迟队列所绑定的交换机
*/
@Bean
public DirectExchange orderTtlDirect() {
return ExchangeBuilder
.directExchange(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange())
.durable(true)
.build();
}
/**
* 将订单延迟队列绑定到交换机
*/
@Bean
Binding orderTtlBinding(
@Qualifier("orderTtlDirect") DirectExchange orderTtlDirect,
@Qualifier("orderTtlQueue") Queue orderTtlQueue
) {
return BindingBuilder
.bind(orderTtlQueue)
.to(orderTtlDirect)
.with(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey());
}
/* 延时队列 end */
/* 死信队列 start */
/**
* 死信队列
*/
@Bean
public Queue orderQueue() {
return QueueBuilder.durable(QueueEnum.QUEUE_ORDER_CANCEL.getQueueName()).build();
}
/**
* 死信交换机
*/
@Bean
public DirectExchange orderDirect() {
return ExchangeBuilder
.directExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange())
.build();
}
/**
* 绑定死信关系
*/
@Bean
Binding orderBinding(
@Qualifier("orderDirect") DirectExchange orderDirect,
@Qualifier("orderQueue") Queue orderQueue
) {
return BindingBuilder
.bind(orderQueue)
.to(orderDirect)
.with(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey());
}
/* 死信队列 end */
/**
* 自定义mq消息转换 , 确保监听端可以接收任何类型的消息
*/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplate;
}
import cn.hutool.core.map.MapUtil;
import com.demo.common.enums.QueueEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 订单消息的发送者
*/
@Slf4j
@Component
public class OrderSender {
@Autowired
private AmqpTemplate amqpTemplate;
@Value("${mq.queue_ttl_order_cancel.exchange}")
private String exchange;
@Value("${mq.queue_ttl_order_cancel.routekey}")
private String routeKey;
/**
* 发送订单生成消息至 MQ
* @param orderId 订单id
* @param delayTimes 存活时间
*/
public void sendOrderMessage(Long orderId, final Long delayTimes) {
//给延迟队列发送消息
Map<String, String> map = MapUtil.newHashMap();
map.put("orderId", orderId.toString());
amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(), QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), map, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//给消息设置有效时间, 时间到达后会将此消息丢入死信队列进行处理
message.getMessageProperties().setExpiration(delayTimes.toString());
return message;
}
});
log.info("-----------超时订单队列发送订单成功 orderId:{}-----------", orderId.toString());
}
}
import com.alibaba.fastjson2.JSONObject;
import com.demo.api.client.service.OrderService;
import com.demo.common.constant.CustomConstants;
import com.demo.common.manager.OrderManager;
import com.demo.common.server.IMessageRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.rabbitmq.client.Channel;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 订单消息的接收者
*/
@Slf4j
@Component
public class OrderReceiver {
@Autowired
private OrderService orderService;
@Autowired
private OrderManager orderManager;
@Autowired
private IMessageRecordService iMessageRecordService;
// 监听死信队列
@RabbitListener(queues = "${mq.queue_ttl_order_cancel.queue}")
@Transactional(rollbackFor = Exception.class)
public void handle(Message message, Channel channel) throws IOException {
byte[] body = message.getBody();
String s = new String(body, StandardCharsets.UTF_8);
JSONObject parse = JSONObject.parse(s);
Long orderId = parse.getLong("orderId");
log.info("-----------超时订单队列开始消费 orderId = {}-----------",orderId.toString());
try {
// 走订单超时的逻辑
orderService.cancelTimeOutOrderMq(orderId);
// 记录超时订单被消费
iMessageRecordService.saveInfo(CustomConstants.ONE_INT, orderId);
// 消息确认成功消费
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
log.info("-----------超时订单队列消息接收成功 orderId:{}-----------",orderId.toString());
} catch (Exception e) {
e.printStackTrace();
// 消息消费失败重新入队
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false,true);
log.error("-----------超时订单队列消息接收失败,重新入队 orderId:{}-----------",orderId.toString());
}
}
}
import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* 退款订单发送者
*/
@Slf4j
@Component
public class RefundOrderSender {
@Autowired
private AmqpTemplate amqpTemplate;
@Value("${mq.refund_queue.exchange}")
private String exchange;
@Value("${mq.refund_queue.routekey}")
private String routeKey;
/**
* 发送退款信息至mq
* @param refundOrderId 退款单id
* @param delayTimes 等待时长
*/
public void sendRefundOrderMessage(Long refundOrderId, final long delayTimes) {
//给延迟队列发送消息
Map<String, String> map = MapUtil.newHashMap();
map.put("refundOrderId", refundOrderId.toString());
amqpTemplate.convertAndSend(exchange, routeKey, map, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//给消息设置延迟毫秒值
message.getMessageProperties().setExpiration(String.valueOf(delayTimes));
return message;
}
});
log.info("-----------退款队列发送消息成功 refundOrderId:{}-----------", refundOrderId.toString());
}
}
import com.alibaba.fastjson2.JSONObject;
import com.demo.api.admin.service.OrderService;
import com.demo.common.constant.CustomConstants;
import com.demo.common.server.IMessageRecordService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 退款订单接收者
*/
@Slf4j
@Component
public class RefundOrderReceiver {
@Autowired
private OrderService orderService;
@Autowired
private IMessageRecordService iMessageRecordService;
@RabbitListener(queues = "${mq.refund_queue.queue}")
@Transactional(rollbackFor = Exception.class)
public void handle(Message message, Channel channel) throws IOException {
byte[] body = message.getBody();
String s = new String(body, StandardCharsets.UTF_8);
JSONObject parse = JSONObject.parse(s);
Long refundOrderId = parse.getLong("refundOrderId");
log.info("-----------退款队列开始消费 refund_order_id = {} -----------", refundOrderId.toString());
try {
orderService.refundOrderAfter(refundOrderId);
// 记录超时订单被消费
iMessageRecordService.saveInfo(CustomConstants.TWO_INT, refundOrderId);
// 消息确认成功消费
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
log.info("-----------退款队列消息接收成功 refundOrderId:{}-----------", refundOrderId.toString());
} catch (Exception e) {
// 消息消费失败重新入队
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
log.error("-----------退款队列消息接收失败,重新入队 refundOrderId:{}-----------", refundOrderId.toString());
throw e;
}
}
}