1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.1.4.RELEASE</version>
</dependency>
2、修改配置文件
spring.rabbitmq.host=192.168.3.10
spring.rabbitmq.port=5672
spring.rabbitmq.virtual-host=/
3、创建配置文件类
@Configuration
public class RabbitConfig {
/**
* 转换接收对象的数据格式,默认是java序列化的格式
* @return
*/
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
}
4、测试
4.1、管理类测试
@Autowired
AmqpAdmin amqpAdmin;
/**
* 声明交换机
*/
@Test
public void createExchange() {
DirectExchange directExchange = new DirectExchange("java.exchange",true,false);
amqpAdmin.declareExchange(directExchange);
}
/**
* 声明队列
*/
@Test
public void createQueue() {
Queue queue = new Queue("java.queue", true, false, false);
amqpAdmin.declareQueue(queue);
}
4.2、消息发送与接收
消息发送:
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void sendMessage(){
rabbitTemplate.convertAndSend("java.exchange","","发送消息成功");
}
消息接收:
/**
* 接收消息的方法必须在容器内
* 第一个参数:message接收的全部内容 : 消息头+消息体
* 第二个参数:接收的消息对象(自定义)
* @param message
*/
@RabbitListener(queues = {"java.queue"})
public void getMessage(Message message, OrderItemEntity orderItemEntity){
byte[] body = message.getBody();
System.out.println("接收到消息:"+message);
}
5、拓展:消息可靠投递
5.1、修改配置文件
#开启发送端确认
spring.rabbitmq.publisher-confirms=true
#开启发送端消息抵达队列的确认
spring.rabbitmq.publisher-returns=true
#只要抵达队列,以异步发送优先回调我们这个returnConfirm
spring.rabbitmq.template.mandatory=true
#手动ack消息
spring.rabbitmq.listener.simple.acknowledge-mode=manual
5.2、修改配置类,设置发送消息的回调
设置消息抵达交换机和队列的回调:
@Configuration
public class RabbitConfig {
@Autowired
RabbitTemplate rabbitTemplate;
/**
* 转换接收对象的数据格式,默认是java序列化的格式
* @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删除这个消息。)
* 1、默认是自动确认的,只要消息被接受到,客户端会自动确认,服务端就会移除这个消息
* 问题:
* 我们收到很多消息,自动回复给服务器ack,只有一个消息处理成功,宕机了。发生消息丢失;
* 消费者手动确认模式:只要我们没有明确告诉MQ,货物被签收;没有ack,消息就一直是unacked状态,即使consumer宕机,
* 消息不会丢失,会重新变为Ready,下一次有新的consumer连接进来就发给他
* 2、如何签收
*
*/
@PostConstruct //相当于RabbitConfig(当前类)创建完成后执行这个方法
public void initRabbitTemplate(){
//设置确认回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* 1、只要消息抵达broker就ack=true
* @param correlationData 当前消息的唯一关联数据(消息的唯一ID)
* @param ack 消息是否成功收到
* @param cause 失败的原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("correlationData="+correlationData+" ack="+ack+" cause="+cause);
}
});
//设置消息抵达队列的确认回调
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
/**
* 只要消息没有抵达指定的队列,就触发这个失败的回调
* @param message 投递失败的消息详细信息
* @param replyCode 回复的状态码
* @param replyText 回复的文本内容
* @param exchange 当时这个消息发给哪个交换机
* @param routingKey 当时这个消息发给哪个路由键
*/
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
}
});
}
}
5.3、消息接收并确认
@RabbitListener(queues = {"java.queue"})
@Service("orderItemService")
public class OrderItemServiceImpl {
/**
* 接收消息的方法必须在容器内 @Service @Component...
* 第一个参数:message接收的全部内容 : 消息头+消息体
* 第二个参数:接收的消息对象(自定义)
* 第三个参数:当前传输数据的通道
* @param message
* @RabbitListener 可以标注在 类 和 方法上
*/
//@RabbitListener(queues = {"java.queue"})
@RabbitHandler
public void getMessage(Message message, OrderItemEntity orderItemEntity, Channel channel){
byte[] body = message.getBody();
System.out.println("接收到消息:"+message);
//channel内按顺序递增
long tag = message.getMessageProperties().getDeliveryTag();
//签收货物,非批量模式
try {
if (tag%2 == 0) {
//收货
channel.basicAck(tag,false);
System.out.println("签收了");
} else {
//退货 requeue=false 丢弃 requeue=true 发回服务器,重新入队
//long deliveryTag, boolean multiple, boolean requeue
channel.basicNack(tag,false,true);
System.out.println("没有签收");
}
} catch (IOException e) {
//网络中断
e.printStackTrace();
}
}
/**
* @RabbitHandler 只能标注在方法上,可以接受不同的实体类,比如下方的getHandlerMessage() 和 getHandlerMessage2()
* 可以接受同一个队列中,不同的对象类型,分类型处理
* @param message
* @param orderItemEntity
* @param channel
*/
@RabbitHandler
public void getHandlerMessage(Message message, OrderItemEntity orderItemEntity, Channel channel){
byte[] body = message.getBody();
System.out.println("接收到消息:"+message);
}
@RabbitHandler
public void getHandlerMessage2( OrderItemEntity orderItemEntity){
System.out.println("接收到消息:"+orderItemEntity);
}
}
注:@RabbitListener可以标注在类和方法上,而@RabbitHandler只能标注在方法上。两者的区别是:
- @RabbitListener标注在方法上时可以单独使用,而@RabbitHandler需要结合@RabbitListener来一起使用(即@RabbitListener标注在类上指明监听的队列,@RabbitHandle标注在方法上,指明监听的队列的类型)
- @RabbitListener只能处理同类型对象的队列,而@RabbitHandle可以处理不同类型对象的队列。