SpringBoot整合RabbitMQ
一、入门
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.young</groupId>
<artifactId>RabbitMQ05</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.4</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
application.yml
spring:
rabbitmq:
host: 192.168.10.131
port: 5672
username: guest
password: guest
virtual-host: /study
publisher-confirm-type: correlated
publisher-returns: true
启动类:
@SpringBootApplication
public class RabbitMQ05Application {
public static void main(String[] args) {
SpringApplication.run(RabbitMQ05Application.class,args);
}
}
订单类:
@Data
public class Order implements Serializable {
private String orderNum;
private Double price;
private LocalDateTime createTime;
private Integer status;
}
首先编写direct交换机
DirectRabbitConfig.class
@Configuration
public class DirectRabbitConfig {
public static String DIRECT_QUEUE="direct_queue";
public static String DIRECT_EXCHANGE="direct_exchange";
public static String DIRECT_ROUTING="direct_routing";
@Bean
public Queue directQueue(){
return new Queue(DIRECT_QUEUE,true);
}
@Bean
public DirectExchange directExchange(){
return new DirectExchange(DIRECT_EXCHANGE,true,false);
}
@Bean
public Binding directBinding(){
return BindingBuilder.bind(directQueue()).to(directExchange()).with(DIRECT_ROUTING);
}
}
DirectConsumer.class
@Component
@RabbitListener(queues = "direct_queue")
public class DirectConsumer {
@RabbitHandler
public void directHandler(String json){
System.out.println("DirectReceiver消费者收到的消息:"+json);
}
}
OrderController.java
@RestController
public class OrderController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/directOrder")
public String directOrder(){
Order order=new Order();
order.setOrderNum(UUID.randomUUID().toString());
order.setPrice(100d);
order.setStatus(0);
order.setCreateTime(LocalDateTime.now());
String json= JSON.toJSONString(order);
//创建订单并入队
rabbitTemplate.convertAndSend(DirectRabbitConfig.DIRECT_EXCHANGE,DirectRabbitConfig.DIRECT_ROUTING,json);
return "ok";
}
}
运行项目
topic主题交换机
TopicRabbitConfig.java
@Configuration
public class TopicRabbitConfig {
public static String USER_FIND="user.find";
public static String USER_ALL="user.all";
public static String TOPIC_EXCHANGE="topic_exchange";
@Bean
public Queue findQueue(){
return new Queue(TopicRabbitConfig.USER_FIND);
}
@Bean
public Queue allQueue(){
return new Queue(TopicRabbitConfig.USER_ALL);
}
@Bean
public TopicExchange topicExchange(){
return new TopicExchange(TOPIC_EXCHANGE);
}
@Bean
public Binding bindingExchangeFind(){
return BindingBuilder.bind(findQueue()).to(topicExchange()).with(USER_FIND);
}
@Bean
public Binding bindingExchangeAll(){
return BindingBuilder.bind(allQueue()).to(topicExchange()).with("user.#");
}
}
TopicFindConsumer.java
@Component
@RabbitListener(queues = "user.find")
public class TopicFindConsumer {
@RabbitHandler
public void process(String msg){
System.out.println("user find 收到消息:"+msg);
}
}
TopicAllConsumer.java
@Component
@RabbitListener(queues = "user.all")
public class TopicAllConsumer {
@RabbitHandler
public void process(String msg){
System.out.println("user all 收到消息:"+msg);
}
}
在OrderController.java中添加两个方法
@GetMapping("/topicFindOrder")
public String findOrder(){
Order order=new Order();
order.setOrderNum(UUID.randomUUID().toString());
order.setPrice(100d);
order.setStatus(0);
order.setCreateTime(LocalDateTime.now());
String json= JSON.toJSONString(order);
rabbitTemplate.convertAndSend(TopicRabbitConfig.TOPIC_EXCHANGE,"user.find",json);
return "ok";
}
@GetMapping("/topicDeleteOrder")
public String deleteOrder(){
Order order=new Order();
order.setOrderNum(UUID.randomUUID().toString());
order.setPrice(100d);
order.setStatus(0);
order.setCreateTime(LocalDateTime.now());
String json= JSON.toJSONString(order);
rabbitTemplate.convertAndSend(TopicRabbitConfig.TOPIC_EXCHANGE,"user.delete",json);
return "ok";
}
运行项目:依次访问topicFindOrder和topicDeleteOrder
观察控制台如下:
Fanout扇形交换机
FanoutRabbitConfig.java
Configuration
public class FanoutRabbitConfig {
public static String FANOUT_EXCHANGE="fanout_exchange";
@Bean
public Queue fanoutQueueA(){
return new Queue("fanout.A");
}
@Bean
public Queue fanoutQueueB(){
return new Queue("fanout.B");
}
@Bean
public Queue fanoutQueueC(){
return new Queue("fanout.C");
}
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange(FANOUT_EXCHANGE);
}
@Bean
public Binding bindingExchangeA(){
return BindingBuilder.bind(fanoutQueueA()).to(fanoutExchange());
}
@Bean
public Binding bindingExchangeB(){
return BindingBuilder.bind(fanoutQueueB()).to(fanoutExchange());
}
@Bean
public Binding bindingExchangeC(){
return BindingBuilder.bind(fanoutQueueC()).to(fanoutExchange());
}
}
FanoutConsumerA.java
@Component
@RabbitListener(queues = "fanout.A")
public class FanoutConsumerA {
@RabbitHandler
public void fanoutHandler(String msg){
System.out.println("接收到fanoutA的消息:"+msg);
}
}
FanoutConsumerB.java
@Component
@RabbitListener(queues = "fanout.B")
public class FanoutConsumerB {
@RabbitHandler
public void fanoutHandler(String msg){
System.out.println("接收到fanoutB的消息:"+msg);
}
}
FanoutConsumerC.java
@Component
@RabbitListener(queues = "fanout.C")
public class FanoutConsumerC {
@RabbitHandler
public void fanoutHandler(String msg){
System.out.println("接收到fanoutC的消息:"+msg);
}
}
在OrderController中添加下列方法
@GetMapping("/fanoutOrder")
public String fanoutOrder(){
Order order=new Order();
order.setOrderNum(UUID.randomUUID().toString());
order.setPrice(100d);
order.setStatus(0);
order.setCreateTime(LocalDateTime.now());
String json= JSON.toJSONString(order);
//创建订单并入队,fanout扇形类似于计网的广播,即会发送所有消息到每一条队列,因此不需要routingkey
rabbitTemplate.convertAndSend(FanoutRabbitConfig.FANOUT_EXCHANGE,null,json);
return "ok";
}
运行项目,访问fanoutOrder
观察控制台如下:
配置确认回调和返回回调
@Configuration
public class RabbitConfig {
@Bean
public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate=new RabbitTemplate();
rabbitTemplate.setConnectionFactory(connectionFactory);
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean b, String s) {
System.out.println("打印确认数据:"+correlationData);
System.out.println("是否确认:"+b);
System.out.println("确认原因:"+s);
}
});
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("回复码:"+returnedMessage.getReplyCode());
System.out.println("回复文本:"+returnedMessage.getReplyText());
System.out.println("返回信息:"+returnedMessage.getMessage());
System.out.println("交换机:"+returnedMessage.getExchange());
System.out.println("路由key:"+returnedMessage.getRoutingKey());
}
});
return rabbitTemplate;
}
}
配置手动确认:
@Configuration
public class MessageListenerConfig {
@Autowired
private CachingConnectionFactory connectionFactory;
@Autowired
private MyAckReceiver myAckReceiver;
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(){
SimpleMessageListenerContainer container=new SimpleMessageListenerContainer(connectionFactory);
container.setConcurrentConsumers(1);//消费者数
container.setMaxConcurrentConsumers(1);//最大消费者数
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);//RabbitMQ默认为自动回复,这里设置手动回复
//设置一个队列
container.setQueueNames("testDirectQueue");
container.setMessageListener(myAckReceiver);
return container;
}
}
@Component
public class MyAckReceiver implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
long deliveryTag=message.getMessageProperties().getDeliveryTag();
try{
byte[]body=message.getBody();
String json=new String(body);
System.out.println("接收的信息为:"+json);
System.out.println("消费的主题来自:"+message.getMessageProperties().getConsumerQueue());
//处理逻辑
//手动确认
channel.basicAck(deliveryTag,true);
}catch (Exception e){
channel.basicAck(deliveryTag,false);
e.printStackTrace();
}
}
}
二、延时队列实现订单过期自动取消
创建一个新项目
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.young</groupId>
<artifactId>RabbitMQ06</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.4</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
</project>
application.yml
spring:
datasource:
username: root
password: 3fa4d180
url: jdbc:mysql://localhost:3306/
rabbitmq:
host: 192.168.10.131
port: 5672
username: guest
password: guest
virtual-host: /study
publisher-confirm-type: correlated
publisher-returns: true
数据库:
实体类:
@TableName(value = "t_order")
@Data
public class Order {
@TableId
private String orderNum;
private Integer productId;
private Integer userId;
private Double price;
private LocalDateTime startTime;
private LocalDateTime endTime;
private Integer status;
}
mapper层
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}
service层
public interface OrderService {
String addOrder(Order order);
Integer cancelOrder(String orderNum);
Integer payOrder(String orderNum);
List<Order> getOrderList(Integer status);
Order getOrderById(String orderNum);
}
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Override
public String addOrder(Order order) {
String orderNum= UUID.randomUUID().toString();
order.setOrderNum(orderNum);
order.setStartTime(LocalDateTime.now());
//订单状态设为0,表示已生成订单但未支付,1表示已支付,2表示已取消
order.setStatus(0);
int addNum=orderMapper.insert(order);
if(addNum>0){
return orderNum;
}
return null;
}
@Override
public Integer cancelOrder(String orderNum) {
UpdateWrapper<Order>updateWrapper=new UpdateWrapper<>();
updateWrapper.eq("order_num",orderNum);
//2表示订单已取消
updateWrapper.set("status",2);
int updateNum=orderMapper.update(null,updateWrapper);
return updateNum;
}
@Override
public Integer payOrder(String orderNum){
UpdateWrapper<Order>updateWrapper=new UpdateWrapper<>();
updateWrapper.eq("order_num",orderNum);
//1表示订单已支付
updateWrapper.set("status",1);
int updateNum=orderMapper.update(null,updateWrapper);
return updateNum;
}
@Override
public List<Order> getOrderList(Integer status) {
QueryWrapper<Order>queryWrapper=new QueryWrapper<>();
queryWrapper.eq("status",status);
List<Order>orderList=orderMapper.selectList(queryWrapper);
return orderList;
}
@Override
public Order getOrderById(String orderNum) {
QueryWrapper<Order>queryWrapper=new QueryWrapper<>();
queryWrapper.eq("order_num",orderNum);
Order order=orderMapper.selectOne(queryWrapper);
return order;
}
}
controller层
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@Autowired
private TTLQueueSender queueSender;
@PostMapping(value = "/addOrder")
public String addOrder(@RequestBody Order order){
String orderNum=orderService.addOrder(order);
if(!Objects.isNull(orderNum)){
//发送订单到延时队列并设置超时为10秒
queueSender.sendMessage(orderNum,10000);
}
return orderNum;
}
@DeleteMapping(value = "/cancelOrder/{orderNum}")
public Integer cancelOrder(@PathVariable String orderNum){
return orderService.cancelOrder(orderNum);
}
@PostMapping(value = "/payOrder/{orderNum}")
public Integer payOrder(@PathVariable String orderNum){
return orderService.payOrder(orderNum);
}
@GetMapping(value = "/getOrderList/{status}")
public List<Order>getOrderList(@PathVariable Integer status){
return orderService.getOrderList(status);
}
@GetMapping(value = "/getOrderById/{orderNum}")
public Order getOrderById(@PathVariable String orderNum){
return orderService.getOrderById(orderNum);
}
}
QueueEnum.java
public enum QueueEnum {
TTL_QUEUE("ttl_queue","ttl_exchange","ttl_routing"),
CANCEL_QUEUE("cancel_queue","cancel_exchange","cancel_routing");
private String queue;
private String exchange;
private String routingKey;
private QueueEnum(String queue,String exchange,String routingKey){
this.queue=queue;
this.exchange=exchange;
this.routingKey=routingKey;
}
public String getQueue(){
return this.queue;
}
public String getExchange(){
return this.exchange;
}
public String getRoutingKey(){
return this.routingKey;
}
}
RabbitMQConfig.java
@Configuration
public class RabbitMQConfig {
//取消订单处理队列
@Bean
public Queue cancelQueue(){
return new Queue(QueueEnum.CANCEL_QUEUE.getQueue());
}
//取消订单交换机
@Bean
public DirectExchange cancelExchange(){
return new DirectExchange(QueueEnum.CANCEL_QUEUE.getExchange());
}
//取消订单绑定
@Bean
public Binding cancelBinding(){
return BindingBuilder.bind(cancelQueue()).to(cancelExchange()).with(QueueEnum.CANCEL_QUEUE.getRoutingKey());
}
//延时队列
@Bean
public Queue ttlQueue(){
return QueueBuilder
.durable(QueueEnum.TTL_QUEUE.getQueue())
.withArgument("x-dead-letter-exchange",QueueEnum.CANCEL_QUEUE.getExchange())
.withArgument("x-dead-letter-routing-key",QueueEnum.CANCEL_QUEUE.getRoutingKey())
.build();
}
//延时交换机
@Bean
public DirectExchange ttlExchange(){
return new DirectExchange(QueueEnum.TTL_QUEUE.getExchange());
}
//绑定
@Bean
public Binding ttlBinding(){
return BindingBuilder.bind(ttlQueue())
.to(ttlExchange())
.with(QueueEnum.TTL_QUEUE.getRoutingKey());
}
}
TTLQueueSender.java
@Component
public class TTLQueueSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(String orderNum){
System.out.println("在"+ LocalDateTime.now().toString()+"时发送订单:"+orderNum);
rabbitTemplate.convertAndSend(QueueEnum.TTL_QUEUE.getExchange(), QueueEnum.TTL_QUEUE.getRoutingKey(),orderNum, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration(String.valueOf(10000));
return message;
}
});
}
public void sendMessage(String orderNum,long expiration){
System.out.println("在"+ LocalDateTime.now().toString()+"时发送订单:"+orderNum);
rabbitTemplate.convertAndSend(QueueEnum.TTL_QUEUE.getExchange(), QueueEnum.TTL_QUEUE.getRoutingKey(), orderNum, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration(String.valueOf(expiration));
return message;
}
});
}
}
CancelOrderReceiver.java
@Component
@RabbitListener(queues = "cancel_queue")
public class CancelOrderReceiver {
@Autowired
private OrderService orderService;
@RabbitHandler
public void process(String orderNum){
System.out.println("在"+ LocalDateTime.now().toString()+"时接收订单:"+orderNum);
Order order=orderService.getOrderById(orderNum);
if (Objects.isNull(order)) {
System.out.println("该订单不存在");
}else{
if(order.getStatus()==0){
//当订单处于已生成但未支付状态时,因为超时,将它取消
int res=orderService.cancelOrder(orderNum);
if(res>0){
System.out.println("取消订单成功");
}else{
System.out.println("取消订单失败");
}
}else{
System.out.println("该订单无需取消");
}
}
}
}
如上面这三张图片所示,订单超时未支付,就会被处理为已取消,接下来我们在创建一个订单,并支付
参考文章:SpringBoot整合RabbitMQ,用心看完这篇就够了
从零开始玩转 RabbitMQ (基于RabbitMQ 3.8.8 + SpringBoot 2.5.7 )