1、pom文件导入坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、application.properties中MQ的配置
# RabbitMQ配置
spring.rabbitmq.host=192.168.200.130
spring.rabbitmq.port=5672
# 虚拟主机配置
spring.rabbitmq.virtual-host=/
# 开启发送端消息抵达Broker确认
spring.rabbitmq.publisher-confirms=true
# 开启发送端消息抵达Queue确认
spring.rabbitmq.publisher-returns=true
# 只要消息抵达Queue,就会异步发送优先回调returnfirm 设不设置都可以
spring.rabbitmq.template.mandatory=true
# 手动ack消息,不使用默认的消费端确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual
3、主启动类
@EnableDiscoveryClient //开启服务注册发现功能
@EnableRabbit//开启RabbitMQ注解
@SpringBootApplication
public class GulimallOrderApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallOrderApplication.class, args);
}
}
4、MyRabbitConfig.java 配置RabbitMQ
package com.acm.gulimall.order.config;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
/**
* @Description
* @Auther: QZ
* @Date: 2023/2/7 - 02 - 07 - 15:51
* @version: 1.0
*/
@Configuration
public class MyRabbitConfig {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 给RabbitMQ发送消息是对象装换成json发送
* 默认是实体类转换成流进行发送
* @return
*/
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
/**
* 定制RabbitTemplate
* 1、服务收到消息就会回调
* 1、spring.rabbitmq.publisher-confirms: true
* 2、设置确认回调
* 2、消息正确抵达队列就会进行回调
* 1、spring.rabbitmq.publisher-returns: true
* spring.rabbitmq.template.mandatory: true
* 2、设置确认回调ReturnCallback
*
* 3、消费端确认(保证每个消息都被正确消费,此时才可以broker删除这个消息)
*
*/
@PostConstruct //MyRabbitConfig对象创建完成以后,执行这个方法
public void initRabbitTemplate() {
/**
* 1、只要消息抵达Broker就ack=true
* correlationData:当前消息的唯一关联数据(这个是消息的唯一id)
* ack:消息是否成功收到
* cause:失败的原因
*/
//设置确认回调
rabbitTemplate.setConfirmCallback((correlationData,ack,cause) -> {
System.out.println("confirm...correlationData["+correlationData+"]==>ack:["+ack+"]==>cause:["+cause+"]");
});
/**
* 只要消息没有投递给指定的队列,就触发这个失败回调
* message:投递失败的消息详细信息
* replyCode:回复的状态码
* replyText:回复的文本内容
* exchange:当时这个消息发给哪个交换机
* routingKey:当时这个消息用哪个路邮键
*/
rabbitTemplate.setReturnCallback((message,replyCode,replyText,exchange,routingKey) -> {
System.out.println("Fail Message["+message+"]==>replyCode["+replyCode+"]" +
"==>replyText["+replyText+"]==>exchange["+exchange+"]==>routingKey["+routingKey+"]");
});
}
}
5、测试类,创建消息生产者
创建交换机,队列,binging,同时创建生产者
package com.acm.gulimall.order;
import com.acm.gulimall.order.entity.OrderEntity;
import com.acm.gulimall.order.entity.OrderReturnApplyEntity;
import com.acm.gulimall.order.entity.OrderReturnReasonEntity;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.annotation.Order;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Date;
import java.util.UUID;
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
public class GulimallOrderApplicationTests {
@Autowired
private AmqpAdmin amqpAdmin;
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 给队列发送消息
*/
@Test
public void sendMessage(){
//发送字符串
String msg = "hello word!";
for (int i = 0; i < 10; i++) {
//发送对象
if(i%2==0){
OrderReturnReasonEntity returnReasonEntity = new OrderReturnReasonEntity();
returnReasonEntity.setId(1L);
returnReasonEntity.setCreateTime(new Date());
returnReasonEntity.setName("hh-"+i);
rabbitTemplate.convertAndSend("hello-java-exchange","hello-java",returnReasonEntity);
}else {
OrderEntity orderEntity = new OrderEntity();
orderEntity.setOrderSn(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend("hello-java-exchange","hello-java",orderEntity);
}
}
}
/**
* 1、如何创建Exchange、Queue、Binding
* 1)、使用AmqpAdmin进行创建
* 2、如何收发消息
*/
@Test
public void createExchange() {
/**
* String name, boolean durable, boolean autoDelete, Map<String, Object> arguments
*/
DirectExchange directExchange = new DirectExchange("hello-java-exchange", true, false);
amqpAdmin.declareExchange(directExchange);
log.info("Exchange[{}]创建成功","hello-java-exchange");
}
/**
* String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
* boolean durable:是否持久化,就是开关机都存在
* boolean exclusive:是否排他,只能进行和一个交换机进行绑定
*/
@Test
public void createQueue(){
Queue queue = new Queue("hello-java-queue",true,false,false);
amqpAdmin.declareQueue(queue);
log.info("Queue[{}]创建成功","hello-java-queue");
}
/**
* String destination, 【目的地】
* Binding.DestinationType destinationType, 【目的地类型】
* String exchange, 【交换机】
* String routingKey, 【绑定的键】
* Map<String, Object> arguments【参数】
*/
@Test
public void createBinging(){
Binding binding = new Binding("hello-java-queue",
Binding.DestinationType.QUEUE,
"hello-java-exchange",
"hello-java",null);
amqpAdmin.declareBinding(binding);
log.info("Binding[{}]创建成功","hello-java");
}
}
6、服务端,消息接收者
package com.acm.gulimall.order.service.impl;
import com.acm.gulimall.order.entity.OrderEntity;
import com.acm.gulimall.order.entity.OrderReturnReasonEntity;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.acm.common.utils.PageUtils;
import com.acm.common.utils.Query;
import com.acm.gulimall.order.dao.OrderItemDao;
import com.acm.gulimall.order.entity.OrderItemEntity;
import com.acm.gulimall.order.service.OrderItemService;
@RabbitListener(queues = {"hello-java-queue"})
@Service("orderItemService")
public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OrderItemEntity> implements OrderItemService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<OrderItemEntity> page = this.page(
new Query<OrderItemEntity>().getPage(params),
new QueryWrapper<OrderItemEntity>()
);
return new PageUtils(page);
}
/**
* queues:声明需要监听的队列
* channel:当前传输数据的通道
* 获取实际消息内容有两种方式:
* 方式一:在方法参数列表中直接声明出来
* 方式二:从请求体中取出消息的二进制形式,然后通过JSON反序列化即可
*/
//@RabbitListener(queues = {"hello-java-queue"})
@RabbitHandler
public void revieveMessage(Message message, OrderReturnReasonEntity entity, Channel channel) {
// 请求体,序列化存储(本例中已使用Jackson2JsonMessageConverter序列化器作JSON序列化存储)
byte[] body = message.getBody();
// 请求头
MessageProperties messageProperties = message.getMessageProperties();
// JSON反序列得到消息内容对象
OrderReturnReasonEntity reason = JSONObject.parseObject(body, OrderReturnReasonEntity.class);
System.out.println("接受到的消息对象" + message);
System.out.println("接受到的消息内容" + reason);
System.out.println("接受到的消息内容" + entity);
}
@RabbitHandler
public void revieveMessage(Message message, OrderEntity entity, Channel channel) {
// 请求体,序列化存储(本例中已使用Jackson2JsonMessageConverter序列化器作JSON序列化存储)
byte[] body = message.getBody();
// 请求头
MessageProperties properties = message.getMessageProperties();
// channel内按顺序自增的long类型消息标签
long deliveryTag = properties.getDeliveryTag();
// JSON反序列得到消息内容对象
OrderEntity reason = JSONObject.parseObject(body, OrderEntity.class);
System.out.println("接受到的消息对象" + message);
System.out.println("接受到的消息内容" + reason);
System.out.println("接受到的消息内容" + entity);
try {
System.out.println(deliveryTag);
if (deliveryTag % 2 == 0) {
// 手动确认,消息会从unacked中删除,total数量减1
// boolean multiple:是否批量签收
channel.basicAck(deliveryTag, false);
System.out.println("消息接受成功:"+deliveryTag);
} else {
// 手动拒签
// boolean multiple:是否批量拒签
// boolean requeue:当前拒签消息是否发回服务器重新入队
// 重新入队的消息会继续发送 等待条件满足进行消费
channel.basicNack(deliveryTag, false, true);
System.out.println("消息拒签:"+deliveryTag);
}
} catch (IOException e) {
// 网络中断
e.printStackTrace();
}
}
}