七.Boot整合RabbitMQ
整合思路:
- 引入 spring-boot-starter-amqp
- 2.application.yml配置
- 測試RabbitMQ
AmqpAdmin:管理組件
RabbitTemplate:消息發送處理組件
1. 創建boot工程,加入rabbitmq啓動器
2. 在配置文件中加入rabbintmq 的配置信息
spring.rabbitmq.addresses=192.168.50.128
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
#spring.rabbitmq.virtual-host=/
3. 使用RabbitTemplate操作rabbitMq
RabbitAutoConfiguration,ConnectionFactory,RabbitProperties自動配置;
注入RabbitTemplate:給rabbitmq發送和接收消息
AmqpAdmin: RabbitMq系統管理功能(聲明隊列,創建交換器)
4. 測試RabbitMQ的發送和接收消息
@RunWith(SpringRunner.class)
@SpringBootTest
public class AmqpApplicationTests {
//注入template模版
@Autowired
RabbitTemplate rt;
@Test
public void contextLoads() {
//點對點發送
rt.convertAndSend("exchanges.direct","oracle","我是一隻小小的石頭,i am a small small stone");
//向fanout發送消息
rt.convertAndSend("exchanges.fanout","oracle","我是一隻小小的fanout,i am a small small fanout");
//給topic發送消息
rt.convertAndSend("exchanges.topic","oracle.abc","我是一隻小小的topic,i am a small small topic");
}
@Test
public void receive(){
//接收隊列的消息
Object obj=rt.receiveAndConvert("oracle");
System.out.println(obj);
}
}
/*定義一個發消息的Service*/
package com.oracle.messageService;
import com.oracle.vo.Book;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderMessage {
@Autowired
RabbitTemplate rabbitTemplate;
public void send(String message){
Object obj= rabbitTemplate.convertSendAndReceive("amq.direct","oracle",message);
System.out.println("結果是:"+obj);
}
public void send(Book book){
Object obj= rabbitTemplate.convertSendAndReceive("order.topic","oracle.news",book);
System.out.println("結果是:"+obj);
}
public void receive(){
Object obj=rabbitTemplate.receiveAndConvert("oracle.over");
System.out.println("obj:"+obj);
}
}
5.配置消息轉換器
打開RabbitTemplate源碼後,找到MessageConverter類,然後在IDEA中使用ctrl+H查看到繼承關係後,找到
Jackson2JsonMessageConverter;並在配置文件中進行配置;
@Configuration
public class MessageConvertor {
@Bean
public MessageConverter jacksonMessageConvertor(){
Jackson2JsonMessageConverter jackson2JsonMessageConverter=new Jackson2JsonMessageConverter();
return jackson2JsonMessageConverter;
}
}
注:如果配置了消息轉換器,那麼使用@RabbitListener也需要配置相同的轉換器,否則會轉換失敗;
6.** 編寫service監聽隊列
@RabbitListener(queues = “oracle”)
此註解放在方法上,監聽隊列
- 啓動類
@EnableRabbit //啓動rabbit
@SpringBootApplication
public class AmqpApplication {
public static void main(String[] args) {
SpringApplication.run(AmqpApplication.class, args);
}
}
- service類
@Service
public class RabbitService {
@RabbitListener(queues = "oracle")
public void oracle(String str) {
System.out.println("收到了oracle隊列中的 消息:" + str);
}
@RabbitListener(queues = "book")
public void book(Book book){
System.out.println("收到一本書: "+book);
}
}
-
配置類
spring.rabbitmq.addresses=127.0.0.1 spring.rabbitmq.username=admin spring.rabbitmq.password=tiger spring.rabbitmq.virtual-host=order
7.特殊說明
消息接收者和消息發送查應該使用相同的消息轉換器;默認是字節序列化。如果要改成json方式的,發送與接收到應該配置json消息轉換器;
8.管理類;
@Autowired
AmqpAdmin amqpAdmin;
@Test
public void create(){
//創建一個exchange
amqpAdmin.declareExchange(new DirectExchange("amqp.direct"));
//創建一個隊列
amqpAdmin.declareQueue(new Queue("amqp.test2"));
//綁定交換器
amqpAdmin.declareBinding(new Binding("amqp.test", Binding.DestinationType.QUEUE,"amqp.direct","qmqp.test",null));
}
Bingding構造方法的參數:
Amqp.test:隊列名
Binding.DestinationType.QUEUE:amqp.test的類型是隊列
Amqp.direct:exchange名稱
Qmqp.test:路由鍵
Null:叄數
操作步驟:
- 每執行完一個操作後,都去後臺管理去查看一下操作後的結果;
- 創建對象使用declaredXXX,刪除對象使用deleteXXX
- 通過查看源碼的方式來操作,使用ctrl+n來查找類或接口,再使用ctrl+h查看類的層次結構;
八.RabbitMQ中的消息確認ACK機制
什麼是消息確認ACK?
如果在處理消息的過程中,消費者的服務器在處理消息時出現異常,那可能這條正在處理的消息就沒有完成消息消費,數據就會丟失。爲了確保數據不丟失,RabbitMQ支持消息確認,即ACK
ACK的消息確認機制
ACK機制是消費者從RabbitMQ收到消息並處理完成後,反饋給RabbitMQ,RabbitMQ收到反饋後纔將次消息從隊列中刪除。
- 如果一個消費者在處理消息出現了網絡不穩定、服務器異常等現象,那麼就不會有ACK反饋,RabbitMQ會認爲這個消息沒有正常消費,會將消息重新放入隊列中。
- 如果在集羣的情況下:RabbitMQ會立刻將這個消息推送給這個在線的其他消費者。這種機制保證了在消費者服務端故障的時候,不丟失任何消息和任務。
- **消息永遠不會從RabbitMQ中刪除:**只有當消費者正確返送ACK反饋,RabbitMQ確認收到後,消息纔會從RabbitMQ服務的數據中刪除。
- 消息的ACK確認機制默認是打開的。
ACK機制的開發注意實現
如果忘記了ACK,那麼後果很嚴重。當Consumer退出時,Message會一直重新分發。然後RabbitMQ會佔用越來越多的內存,由於RabbitMQ會長時間運行,因此這個“內存泄漏”是致命的。
練習:
創建兩個模塊,消費者和生產者使用direct交換器,在消費者消費消息時拋出一個RuntimeException,觀察控制檯和RabbitMQ的Web控制檯,消息會再次重新放到隊列中,執行死循環。
receiver:
@Component
@RabbitListener(bindings=@QueueBinding(
value=@Queue(value="${mq.config.queue.info}",autoDelete = "true"),
exchange = @Exchange(value="${mq.config.exchange}",type= ExchangeTypes.DIRECT),
key = "${mq.config.queue.info.routing.key}"
))
public class InfoReceiver {
@RabbitHandler
public void process(String msg){
System.out.println("---------consumer:receive:"+msg);
throw new RuntimeException();
}
}
RabbitMQ web控制檯
解決方式:
方式一:可以在消費消息中try-catch
由於在消費消息時可能拋出運行時異常,這種解決方式難於處理和控制。
方式二:在Consumer的配置文件中配置重試機制
配置開啓重試及重試次數,在RabbitMQ沒有收到ACK確認時,重新放入隊列,重試指定次數後不再放入隊列。
spring.application.name=mq-direct-consumer
spring.rabbitmq.host=192.168.37.151
spring.rabbitmq.port=5672
spring.rabbitmq.username=testww
spring.rabbitmq.password=000000
#開啓重試機制
spring.rabbitmq.listener.simple.retry.enabled=true
#重試次數 默認爲3次 現修改爲5次
spring.rabbitmq.listener.simple.retry.max-attempts=5