SpringBoot整合RabbitMQ
1.引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
- 引入AMQP场景,RabbitAutoConfiguration就会自动生效
- RabbitAutoConfiguration会给容器中自动配置RabbitTemplate、AmqpAdmin、CachingConnectionFactory、RabbitMessagingTemplate等组件
2.添加配置文件
spring:
rabbitmq:
host: 47.106.xxx.xxx
port: 5672
virtual-host: / #虚拟主机,默认/
3.开启RabbitMQ功能 @EnableRabbit
@EnableRabbit
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
4.创建队列、交换机、绑定关系
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderApplicationTests {
@Autowired
AmqpAdmin amqpAdmin;
/**
* 创建队列
*/
@Test
public void createQueue() {
/**
* Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
* name : 队列名称
* durable : 是否持久化
* exclusive : 是否排它,如果有一条链接使用了这个队列,其他链接就无法使用
* autoDelete : 是否自动删除
*/
Queue queue = new Queue("hello-java-queue", true, false, false, null);
amqpAdmin.declareQueue(queue);
}
/**
* 创建交换机
*/
@Test
public void createExchange() {
/**
* DirectExchange(String name, boolean durable, boolean autoDelete, Map<String, Object> arguments)
* name : 交换机名称
* durable : 是否持久化
* autoDelete : 是否自动删除
*/
Exchange exchange = new DirectExchange("hello-java-exchange", true, false, null);
amqpAdmin.declareExchange(exchange);
}
/**
* 创建绑定关系
*/
@Test
public void createBinging() {
/**
* Binding(String destination, Binding.DestinationType destinationType, String exchange, String routingKey, Map<String, Object> arguments)
* destination : 目的地(队列名称或者交换机名称)
* DestinationType : 目的地类型
* exchange : 交换机
* routingKey : 路由键
* 将exchange指定的交换机和destination目的地进行绑定,使用routingKey作为路由键
*/
Binding binding = new Binding("hello-java-queue", Binding.DestinationType.QUEUE, "hello-java-exchange", "hello.java", null);
amqpAdmin.declareBinding(binding);
}
}
5.生产端发送消息
@RunWith(SpringRunner.class)
@SpringBootTest
public class OrderApplicationTests {
@Autowired
RabbitTemplate rabbitTemplate;
/**
* 发送消息
*/
@Test
public void sendMessage() {
/**
* convertAndSend(String exchange, String routingKey, Object object)
* 1.如果发送的消息是一个对象,我们会使用序列化机制,将对象写出去。对象必须实现Serializable接口
* 2.如果发送的对象要是一个json格式,修改消息发送类型MessageConverter
* 3.new CorrelationData(UUID.randomUUID().toString()) 指定发送消息的唯一id
*/
//String msg = "Hello word!";
OrderEntity msg = new OrderEntity();
msg.setOrderSn(UUID.randomUUID().toString());
msg.setCommentTime(new Date());
rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", msg, new CorrelationData(UUID.randomUUID().toString()));
}
}
6.创建RabbitConfig
- 如果发送的对象要是一个json格式,修改消息发送类型MessageConverter
@Configuration
public class MyRabbitConfig {
/**
* 使用JSON序列化机制,进行消息转换
*
* @return
*/
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
}
7.消费的消费消息
@RabbitListener 标注类或者方法上(监听哪些队列)
@RabbitHandler 标注在方法上(重载方法进行区分不同的消息类型)
/**
* queues 需要监听的队列
*/
@Component
@org.springframework.amqp.rabbit.annotation.RabbitListener(queues = {"hello-java-queue"})
public class RabbitListener {
/**
* org.springframework.amqp.core.Message
* 参数可以写以下类型
* 1.Message message原生消息类型 头+体
* 2.T<发送的消息类型> OrderEntity order
* 3.Channel channel : 当前传输数据的通道
* <p>
* Queue 可以有很多人监听,只要收到消息 队列就会删除消息,而且只能有一个人收到消息
* 场景:
* 1.订单服务启动多个服务。同一个消息,只能有一个客户端收到消息
* 2.只有一个消息完全处理完,方法运行结束,我能就可以接收到下一个消息
*
* @param message
*/
@RabbitHandler
public void receiverMessage(Message message, OrderEntity order, Channel channel) {
System.out.println("Message===>" + message + "OrderEntity==>" + order);
}
}
8.消息可靠投递
添加配置文件信息
spring:
rabbitmq:
host: 47.106.130.110
port: 5672
virtual-host: /
publisher-confirms: true #开启发送端消息确认
publisher-returns: true #开启发送端消息抵达队列确认
template:
mandatory: true #只要抵达队列,以异步发送优先回调我们这个returnConfirm
listener:
simple:
acknowledge-mode: manual #设置消费端手动确认消息
8.1保证消费端发送消息到broker端
服务端broker收到消息就回调
@Configuration
public class MyRabbitConfig {
@Autowired
RabbitTemplate rabbitTemplate;
/**
* 定制rabbitTemplate
* @PostConstruct MyRabbitConfig对象创建完成,执行这个方法
*/
@PostConstruct
public void initRabbitTemplate() {
//设置确认回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* 只要消息抵达Broker就ack=ture
* @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);
if(!ack){
System.out.println("发送失败....");
}
}
});
}
}
8.2. 保证服务端正确发送消息到队列
消息正确抵达队列进行回调
@Configuration
public class MyRabbitConfig {
@Autowired
RabbitTemplate rabbitTemplate;
/**
* 定制rabbitTemplate
* @PostConstruct MyRabbitConfig对象创建完成,执行这个方法
*/
@PostConstruct
public void initRabbitTemplate() {
//设置生产者发送消息确认回调
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* 只要消息抵达Broker就ack=ture
* @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);
if(!ack){
System.out.println("发送失败....");
}
}
});
//设置消息抵达队列的回调,发送消息到队列失败回调
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) {
System.out.println("Fail Massage==>" + message + "replyCode==>" + replyCode + "replyText==>" + replyText + "exchange==>" + exchange + "routingKey==>" + routingKey);
}
});
}
}
8.3. 保证消费端收到消息
/**
* queues 需要监听的队列
*/
@Component
@org.springframework.amqp.rabbit.annotation.RabbitListener(queues = {"hello-java-queue"})
public class RabbitListener {
/**
* @param message
*/
@RabbitHandler
public void receiverMessage(Message message, OrderEntity order, Channel channel) {
System.out.println("Message===>" + message + "OrderEntity==>" + order);
MessageProperties messageProperties = message.getMessageProperties();
long deliveryTag = messageProperties.getDeliveryTag();
try {
/**
* deliveryTag 消息发送的序号
* multiple 是否批量签收 false非批量签收,true批量签收
*/
channel.basicAck(deliveryTag, false);
} catch (IOException e) {
try {
/**
* deliveryTag 消息发送的序号
* multiple 是否批量签收 false非批量签收,true批量签收
* requeue 是否重回队列 true重回队列 false不重回队列,丢弃
*/
channel.basicNack(deliveryTag,false,false);
} catch (IOException ex) {
}
}
}
}