初步了解了RabbitMQ的流程、工作模式后,进行RabbitMQ与SpringBoot整合
一、相关依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
二、生产者
1、生产者 application.properties
spring:
rabbitmq:
host: 192.168.93.138
port: 5672
username: guest
password: guest
publisher-confirms: true #交换机确认消息机制
publisher-returns: true #队列确认消息机制
virtualHost: / #指定连接MQ的虚拟机名,MQ的虚拟机名以"/"开头
2、声明交换机(生产者仅需创建交换机,队列由消费者创建)
@Configuration
public class RabbitMQConfig {
//交换机的名称
public static final String EX_ROUTING_CMS_POSTPAGE="ex_routing_cms_postpage";
/**
* 声明交换机,交换机配置使用direct类型
* @return the exchange
*/
@Bean(EX_ROUTING_CMS_POSTPAGE)
public Exchange EXCHANGE_TOPICS_INFORM() {
return ExchangeBuilder.directExchange(EX_ROUTING_CMS_POSTPAGE).durable(true).build();
}
}
3、消息发送确认配置
1)、application.properties添加配置
spring:
rabbitmq:
publisher-confirms: true #交换机确认消息机制
publisher-returns: true #队列确认消息机制
2)、确认生产者发送的消息是否到达交换机,成功时打印语句
@Component
public class RabbitMQ_ExConfirm_Config implements RabbitTemplate.ConfirmCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct //jdk的注解 在@Autowired之后执行,只会执行一次
public void init(){
rabbitTemplate.setConfirmCallback(this);
}
@Override
public void confirm(CorrelationData correlationData, boolean ack, String msg) {
System.out.println("------交换机确认机制confirm------");
System.out.println("消息唯一标识:" + correlationData);
System.out.println("确认结果:" + ack);
System.out.println("失败原因:" + msg);
}
}
3)、确认生产者发送的消息是否到达队列,失败时会打印语句
@Component
public class RabbitMQ_Queue_Config implements RabbitTemplate.ReturnCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init(){
rabbitTemplate.setReturnCallback(this);
}
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("------队列确认机制returnedMessage------");
System.out.println("消息主体 message:" + message);
System.out.println("返回代码 replyCode:" + replyCode);
System.out.println("描述 replyCode:" + replyText);
System.out.println("交换机 exchange:" + exchange);
System.out.println("路由key routingKey:" + routingKey);
}
}
4、使用rabbitTemplate发送消息
/**
* 1、交换机名称:RabbitMQConfig.EX_ROUTING_CMS_POSTPAGE
* 2、路由key: 站点ID
* 3、消息对象:页面ID
*/
rabbitTemplate.convertAndSend(RabbitMQConfig.EX_ROUTING_CMS_POSTPAGE, getById(pageId).getSiteId(), jsonString);
三、消费者
1、消费者 application.properties
spring:
rabbitmq:
host: 192.168.93.138
port: 5672
username: guest
password: guest
listener:
simple:
acknowledge-mode: MANUAL #手动返回ACK
virtualHost: /
2、队列、交换机的声明与绑定
@Configuration
public class RabbitMQConfig {
//队列bean的名称
public static final String QUEUE_CMS_POSTPAGE = "queue_cms_postpage";
//交换机的名称
public static final String EX_ROUTING_CMS_POSTPAGE="ex_routing_cms_postpage";
//队列的名称
@Value("${xuecheng.mq.queue}")
public String queue_cms_postpage_name;
//routingKey 即站点Id
@Value("${xuecheng.mq.routingKey}")
public String routingKey;
/**
*声明交换机,交换机配置使用direct类型
* @return the exchange
*/
@Bean(EX_ROUTING_CMS_POSTPAGE)
public Exchange EXCHANGE_TOPICS_INFORM() {
return ExchangeBuilder.directExchange(EX_ROUTING_CMS_POSTPAGE).durable(true).build();
}
//声明队列
@Bean(QUEUE_CMS_POSTPAGE)
public Queue QUEUE_CMS_POSTPAGE() {
Queue queue = new Queue(queue_cms_postpage_name);
return queue;
}
/**
* 绑定队列到交换机
*
* @param queue the queue
* @param exchange the exchange
*
* @return the binding
*/
@Bean
public Binding BINDING_QUEUE_INFORM_SMS(@Qualifier(QUEUE_CMS_POSTPAGE) Queue queue,
@Qualifier(EX_ROUTING_CMS_POSTPAGE) Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
}
}
3、监听并接收队列消息
@Component
public class ConsumerPostPage {
private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerPostPage.class);
@Autowired
PageService pageService;
//监听队列
@RabbitListener(queues = {"${xuecheng.mq.queue}"})
public void postPage(String msg, Message message, Channel channel) throws IOException, InterruptedException {
//消息投递序号,每次消费或者重新投递requeue后,delivery_tag都会增加(每个channel通道都有)
long deliveryTag = message.getMessageProperties().getDeliveryTag();
/**解析消息,因为队列中的消息格式是json格式
* 消息格式:
* {
* "pageId":""
* }
*/
try{
System.out.println("接收到消息:" + message);
Map map = JSON.parseObject(msg, Map.class);//将json字符串转换成Map格式
//获取页面ID
String pageId = map.get("pageId").toString();
//校验页面ID是否合法
CmsPage cmsPage = pageService.findCmsById(pageId);
if(null==cmsPage){
LOGGER.error("页面ID对应页面不存在");
return;
}
//调用savePageToServerPath方法使程序下载html文件到服务器的指定物理路径
pageService.savePageToServerPath(pageId);
/**
* 处理消息过程没有出现异常,手动发送消息接收成功
* deliveryTag:该消息的index
* multiple:是否批量. true:将一次性ack所有小于deliveryTag的消息。
*/
Thread.sleep(1000);
System.out.println("成功确认消息");
channel.basicAck(deliveryTag, false);
}
catch (Exception e){
/**
* 处理消息过程出现异常,手动发送消息接收失败
* 1、deliveryTag:该消息的index。
* 2、multiple:是否批量. true:将一次性拒绝所有小于deliveryTag的消息。
* 3、requeue:被拒绝的是否重新入队列。
*/
Thread.sleep(1000);
System.out.println("处理队列消息发生异常,重新将该消息抛进队列");
System.out.println("deliveryTag:" + deliveryTag);
channel.basicNack(deliveryTag, false, true);
}
}
}
总结
使用RabbitMQ的基本流程: