Rabbitmq整合springboot
rabbitmq整合springboot中,配置信息方面的整理
项目搭建
该项目为测试项目的搭建,总共分为四个模块,分别为:父项目、common模块、消息生产者模块、消息消费者模块。
父项目模块(rabbit-pro)
<?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.feng.rabbit</groupId>
<artifactId>rabbit</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>message-pro</module>
<module>message-cus</module>
<module>common</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
父项目模块基于springboot2.2.1.RELEASE版本,具体依赖信息,如上所示。
common模块
项目结构如图:
MsgObj:该对象是消息体,消息生产者模块和消息消费者模块都会用到该对象
package com.feng.rabbit.com.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* @description:
* @author: fenglin
* @create: 2021-08-13 14:24
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MsgObj implements Serializable {
private String msgId;
private String name;
private String addr;
private int age;
private String exchange;
private String routeKey;
}
Constants:定义的消息队列名,交换机名,路由名
package com.feng.rabbit.com.utils;
/**
* @description:
* @author: fenglin
* @create: 2021-08-13 15:11
**/
public interface Constants {
// 队列名
String QUEUE_NAME = "mail.queue1";
// 交换机名
String EXCHANGE_NAME = "mail.exchange1";
// 路由名
String MAIL_ROUTING_KEY = "mail.routing1";
}
消息生产者模块
项目结构图:
RabbitConfig:rabbitmq配置类
package com.feng.rabbit.pro.config;
import com.feng.rabbit.com.utils.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @description:
* @author: fenglin
* @create: 2021-08-13 13:44
**/
@Configuration
public class RabbitConfig {
private final static Logger log = LoggerFactory.getLogger(RabbitConfig.class);
@Autowired
private CachingConnectionFactory cachingConnectionFactory;
@Bean
RabbitTemplate rabbitTemplate () {
RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
// 当消息发送到rabbitmq时,如果消息被rabbitmq收到,rabbitmq会回调下面的方法
// 参数1:回调相关参数
// 参数2:boolean值,true表示mq接受消息成功,false表示接受消息失败
// 参数3:String类型,表示失败原因
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
String msgId = correlationData.getId();// 消息唯一ID
if (ack)
log.info("消息发送成功:{}", msgId);
else
log.warn("消息发送失败:{},失败原因:{}", msgId, cause);
});
// 当消息从交换机发送到对应队列失败时,触发该回调
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
log.warn("交换机发送消息到队列失败:{}", message);
log.warn("交换机发送消息到队列失败replyCode:{}", replyCode);
log.warn("交换机发送消息到队列失败replyText:{}", replyText);
log.warn("交换机发送消息到队列失败exchange:{}", exchange);
log.warn("交换机发送消息到队列失败routingKey:{}", routingKey);
});
return rabbitTemplate;
}
@Bean("mailQueue")
public Queue mailQueue() {
// 参数1:队列名 参数2:是否持久化
return new Queue(Constants.QUEUE_NAME, true);
}
@Bean("mailExchange")
public DirectExchange mailExchange() {
// 参数1:交换机名 参数2:是否持久化 参数3:是否自动删除,如果没有与之绑定的Queue,直接删除 参数4:结构化参数
return new DirectExchange(Constants.EXCHANGE_NAME, true, false, null);
}
@Bean
public Binding mailBinding() {
// 将queue和exchange进行绑定
return BindingBuilder.bind(mailQueue()).to(mailExchange()).with(Constants.MAIL_ROUTING_KEY);
}
}
MailController:用于触发生产者发送消息的接口
package com.feng.rabbit.pro.controller;
import com.feng.rabbit.com.domain.MsgObj;
import com.feng.rabbit.com.utils.Constants;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
/**
* @description:
* @author: fenglin
* @create: 2021-08-13 14:25
**/
@RestController
@RequestMapping(value = "/mail")
public class MailController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping(value = "/sendQueue1")
public String send() {
String msgId = UUID.randomUUID().toString();
MsgObj msgObj = new MsgObj(msgId, "阿木木", "四川成都",
22, Constants.EXCHANGE_NAME, Constants.MAIL_ROUTING_KEY);
// 参数1:交换机名
// 参数2:路由名
// 参数3:消息体
// 参数4:ConfirmCallback回调信息中的唯一ID
rabbitTemplate.convertAndSend(Constants.EXCHANGE_NAME,
Constants.MAIL_ROUTING_KEY,
msgObj,
new CorrelationData(msgId));
return "success";
}
}
application.yml:springboot配置信息
server:
port: 8090
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
publisher-confirm-type: correlated #开启Rabbitmq发送消息确认机制,发送消息到队列并触发回调方法, 开启confirm回调
publisher-returns: true #可以在消息没有被路由到指定的queue时将消息返回,而不是丢弃 开启returnedMessage回调
# 监听端手动确认机制,只有在监听端配置才有效
# listener:
# simple:
# acknowledge-mode: manual # 开启手动确认模式
消息消费者模块
项目结构:
MailListener:队列监听类,如果没有配置手动确认机制,则按照下列写法是没有问题
package com.feng.rabbit.cus.receiver;
import com.alibaba.fastjson.JSON;
import com.feng.rabbit.com.domain.MsgObj;
import com.feng.rabbit.com.utils.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @description:
* @author: fenglin
* @create: 2021-08-13 14:58
**/
@Component
public class MailListener {
private final static Logger log = LoggerFactory.getLogger(MailListener.class);
@RabbitListener(queues = Constants.QUEUE_NAME)
public void mailHandler(MsgObj msgObj) {
log.info("接收到队列({})的消息为:{}", Constants.QUEUE_NAME, JSON.toJSONString(msgObj));
}
}
如果配置了手动确认机制,需要按照如下写法:
package com.feng.rabbit.cus.receiver;
import com.alibaba.fastjson.JSON;
import com.feng.rabbit.com.domain.MsgObj;
import com.feng.rabbit.com.utils.Constants;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @description:
* @author: fenglin
* @create: 2021-08-13 14:58
**/
@Component
public class MailListener {
private final static Logger log = LoggerFactory.getLogger(MailListener.class);
@RabbitListener(queues = Constants.QUEUE_NAME)
public void mailHandler(MsgObj msgObj, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) {
log.info("接收到队列({})的消息为:{}", Constants.QUEUE_NAME, JSON.toJSONString(msgObj));
try {
channel.basicAck(tag, false); // 手动确认消息
} catch (IOException e) {
e.printStackTrace();
}
}
}
application.yml:springboot配置文件
server:
port: 8091
spring:
rabbitmq:
host: 192.168.71.128
port: 5672
username: guest
password: guest
# 监听端手动确认机制
listener:
simple:
acknowledge-mode: manual # 开启手动确认模式
消费端是开启了手动确认模式的。
补充一点:开启手动确认模式和没有开启手动确认模式有什么区别呢?
如果没有开启手动确认模式,消息队列会直接将消息发给对应的监听,无需确认手动确认;
开启手动确认模式后,必须要手动执行channel.basicAck(tag, false),否则在下一次启动消费者端时,消息队列会再次把之前没有手动确认过的消息发送到监听器中,也就是说,消息堆积在消息队列中,极端情况是会将消息队列中的内存沾满,造成消息队列崩溃。
在rabbitmq客户端,通过对应的队列,就可以看到没有确认的消息数。
功能测试
通过浏览器访问http://localhost:8090/mail/sendQueue1
消息生产者:
注意:消息生产者打印出来的ID,这是我们发送消息时,设置的ID。
String msgId = UUID.randomUUID().toString();
MsgObj msgObj = new MsgObj(msgId, "冯林", "四川南充",
22, Constants.EXCHANGE_NAME, Constants.MAIL_ROUTING_KEY);
// 参数1:交换机名 参数2: 路由名 参数3:消息体 参数4:ConfirmCallback回调信息中的唯一ID
rabbitTemplate.convertAndSend(Constants.EXCHANGE_NAME,
Constants.MAIL_ROUTING_KEY,
msgObj,
new CorrelationData(msgId));
消息消费者:
项目代码已上传到GitEE:https://gitee.com/fenglin676168/rabbitmq.git