Spring Boot RabbitMQ 應(yīng)用場(chǎng)景
1. 前言
消息隊(duì)列是一個(gè)容器,可以對(duì)程序產(chǎn)生的消息進(jìn)行存儲(chǔ)。消息隊(duì)列的主要用途是削峰、異步、解耦,我們用一個(gè)實(shí)際場(chǎng)景來(lái)解釋下。
有一家果汁生產(chǎn)企業(yè),張三是采購(gòu)員,負(fù)責(zé)采購(gòu)水果;李四、趙五是配送員,分別負(fù)責(zé)將蘋(píng)果、香蕉配送到生產(chǎn)車(chē)間。
1.1 削峰
傳統(tǒng)模式下,張三采購(gòu)?fù)瓿?,回到公司后,?lián)系李四、趙五配送采購(gòu)的水果。但是隨著公司業(yè)務(wù)量大增,張三一次性采購(gòu)的水果,李四、趙五得需要幾天才能配送完。所以需要一個(gè)倉(cāng)庫(kù),張三采購(gòu)?fù)瓿芍苯臃诺絺}(cāng)庫(kù)里,李四、趙五慢慢從倉(cāng)庫(kù)取出配送。
此處的倉(cāng)庫(kù)就是消息隊(duì)列,張三是采購(gòu)消息的生產(chǎn)者,李四、趙五是消費(fèi)者。當(dāng)生產(chǎn)的消息太多時(shí),可以使用隊(duì)列削峰
,這樣消費(fèi)者可以慢慢處理消息。
1.2 異步
傳統(tǒng)模式下,張三采購(gòu)?fù)瓿珊?,需要等待李四、趙五來(lái)取,實(shí)際上極大浪費(fèi)了張三的時(shí)間。如果直接放入倉(cāng)庫(kù),可以不必等待,直接進(jìn)行下面的工作。也就是說(shuō),張三與李四、趙五的工作是異步
的,減少了等待時(shí)間。
1.3 解耦
之前張三采購(gòu)?fù)瓿珊?,有?zé)任通知李四、趙五來(lái)取。萬(wàn)一李四、趙五忘帶手機(jī),張三還得聯(lián)系領(lǐng)導(dǎo)協(xié)調(diào)處理,說(shuō)實(shí)話張三就是個(gè)大老粗,整天為這些破事煩得不行。
如果直接放入倉(cāng)庫(kù),張三根本不用管李四、趙五的事情,感覺(jué)愉快極了。張三與李四、趙五的工作不再互相依賴,都變得更加簡(jiǎn)單了,這就是解耦
。
2. RabbitMQ 簡(jiǎn)介
RabbitMQ 是非常出名的消息中間件,遵循 AMQP 協(xié)議,可以跨平臺(tái)、跨語(yǔ)言使用。 RabbitMQ 具備低時(shí)延、高可用的特點(diǎn),還有簡(jiǎn)潔易用的可視化管理界面,所以本節(jié)我們使用 RabbitMQ 來(lái)進(jìn)行消息隊(duì)列技術(shù)的演示。
3. Spring Boot 實(shí)現(xiàn)
我們就針對(duì)上面的場(chǎng)景,使用 Spring Boot ,結(jié)合 RabbitMQ 來(lái)具體實(shí)現(xiàn)下水果采購(gòu)、配送的管理。
3.1 使用 Spring Initializr 創(chuàng)建項(xiàng)目
Spring Boot 版本選擇 2.2.5 ,Group 為 com.imooc , Artifact 為 spring-boot-rabbitmq,生成項(xiàng)目后導(dǎo)入 Eclipse 開(kāi)發(fā)環(huán)境。
3.2 引入項(xiàng)目依賴
我們引入 Web 項(xiàng)目依賴與 AMQP 消息隊(duì)列依賴。
實(shí)例:
<!-- Web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- AMQP 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3.3 配置 RabbitMQ 連接信息
項(xiàng)目創(chuàng)建后,通過(guò) applicaiton.properties 配置 RabbitMQ 的鏈接信息。
實(shí)例:
#地址
spring.rabbitmq.host=127.0.0.1
#端口 默認(rèn)5672
spring.rabbitmq.port=5672
#用戶名
spring.rabbitmq.username=guest
#密碼
sprng.rabbitmq.password=guest
3.4 配置隊(duì)列
首先配置兩個(gè)隊(duì)列,存儲(chǔ)蘋(píng)果采購(gòu)消息、香蕉采購(gòu)消息。
實(shí)例:
/**
* 消息隊(duì)列配置類(lèi)
*/
@Configuration
public class RabbitConfig {
/**
* 蘋(píng)果采購(gòu)消息隊(duì)列
*/
@Bean
public Queue appleQueue() {
return new Queue("apple-queue");
}
/**
* 香蕉采購(gòu)消息隊(duì)列
*/
@Bean
public Queue bananaQueue() {
return new Queue("banana-queue");
}
}
3.5 配置交換機(jī)和綁定
如果消息直接發(fā)到隊(duì)列的話,不夠靈活, RabbitMQ 提供了交換機(jī)與綁定機(jī)制。
消息發(fā)送給交換機(jī),交換機(jī)可以靈活地與隊(duì)列進(jìn)行綁定,這樣消息就可以通過(guò)多種方式進(jìn)入隊(duì)列了。
實(shí)例:
/**
* 配置交換機(jī)
*/
@Bean
TopicExchange exchangeTopic() {
return new TopicExchange("exchange-topic");
}
/**
* 交換機(jī)綁定蘋(píng)果采購(gòu)消息隊(duì)列
*/
@Bean
Binding bindAppleQueue() {
return BindingBuilder.bind(appleQueue()).to(exchangeTopic()).with("#.apple.#");
}
/**
* 交換機(jī)綁定香蕉采購(gòu)消息隊(duì)列
*/
@Bean
Binding bindBananaQueue() {
return BindingBuilder.bind(bananaQueue()).to(exchangeTopic()).with("#.banana.#");
}
我們來(lái)詳細(xì)解釋下交換機(jī)與綁定的運(yùn)行機(jī)制。
- 我們配置了一個(gè)交換機(jī) exchangeTopic ,它可以接收消息。
- 交換機(jī) exchangeTopic 綁定了兩個(gè)隊(duì)列,分別是 appleQueue 和 bananaQueue ,說(shuō)明這兩個(gè)隊(duì)列在關(guān)注該交換機(jī)收到的消息。
- 那么交換機(jī) exchangeTopic 收到的消息到底會(huì)進(jìn)入哪個(gè)隊(duì)列呢,我們發(fā)現(xiàn)交換機(jī)的類(lèi)型是
TopicExchange
,說(shuō)明該交換機(jī)是話題
交換機(jī),隊(duì)列應(yīng)該是獲取其感興趣的話題相關(guān)的消息。 - 當(dāng) appleQueue 隊(duì)列綁定到交換機(jī)時(shí),
with("#.apple.#")
就表示 appleQueue 關(guān)心的是 apple 相關(guān)的話題;而 bananaQueue 關(guān)心的是 banana 相關(guān)的話題。 - 所以可以推斷出,消息在發(fā)送時(shí),可以指定話題相關(guān)的信息,以便消息能被關(guān)注該話題的隊(duì)列接收。
經(jīng)過(guò)上面的分析,我們就知道了消息發(fā)送時(shí)通過(guò)攜帶話題信息,交換機(jī)會(huì)將該消息路由到關(guān)心該話題的隊(duì)列中。
3.6 創(chuàng)建消費(fèi)者
接下來(lái),我們就可以定義消息的消費(fèi)者李四、趙五了。他倆分別關(guān)心蘋(píng)果采購(gòu)消息和香蕉采購(gòu)消息。也就是監(jiān)聽(tīng)蘋(píng)果消息隊(duì)列和香蕉消息隊(duì)列。
實(shí)例:
/**
* 消息隊(duì)列接收
*/
@Component
public class RabbitReceiver {
/**
* lisi負(fù)責(zé)監(jiān)聽(tīng)apple-queue
*/
@RabbitListener(queues = "apple-queue")
public void lisi(String msg) {
System.out.println("李四知道:" + msg);
}
/**
* zhaowu負(fù)責(zé)監(jiān)聽(tīng)banana-queue
*/
@RabbitListener(queues = "banana-queue")
public void zhaowu(String msg) {
System.out.println("趙五知道:" + msg);
}
}
3.7 測(cè)試
運(yùn)行啟動(dòng)類(lèi),從 RabbitMQ 管理界面可以看到已生成指定名稱的隊(duì)列了。
此時(shí)我們定義一個(gè)控制器用于發(fā)起測(cè)試,直接使用 rabbitTemplate 發(fā)送消息即可。
實(shí)例:
@RestController
public class TestController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/test")
public void test() {
// 發(fā)送消息 參數(shù)分別為:交換機(jī)名稱、路由鍵、消息內(nèi)容
rabbitTemplate.convertAndSend("exchange-topic", "apple", "蘋(píng)果來(lái)了10斤");
rabbitTemplate.convertAndSend("exchange-topic", "banana", "香蕉來(lái)了5斤");
rabbitTemplate.convertAndSend("exchange-topic", "apple.banana", "蘋(píng)果來(lái)了8斤;香蕉來(lái)了20斤");
}
}
convertAndSend() 方法的第 1 個(gè)參數(shù)表示交換機(jī),第 2 個(gè)參數(shù)表示路由鍵(消息的話題),第 3 個(gè)是消息內(nèi)容。
所以第 1 個(gè)消息會(huì)被 apple-queue 接收,第 2 個(gè)消息會(huì)被 banana-queue 接收,第 3 個(gè)消息會(huì)被兩個(gè)隊(duì)列接收。
我們啟動(dòng)項(xiàng)目,然后訪問(wèn) http://127.0.0.1:8080/test
,控制臺(tái)輸出如下,驗(yàn)證成功。
趙五知道:香蕉來(lái)了5斤
李四知道:蘋(píng)果來(lái)了10斤
趙五知道:蘋(píng)果來(lái)了8斤;香蕉來(lái)了20斤
李四知道:蘋(píng)果來(lái)了8斤;香蕉來(lái)了20斤
4. 小結(jié)
本小節(jié)通過(guò)一個(gè)實(shí)際應(yīng)用場(chǎng)景,演示了 Spring Boot 中使用 RabbitMQ 消息隊(duì)列的方法。
至此, Spring Boot 的內(nèi)容就全部結(jié)束了。紙上得來(lái)終覺(jué)淺,絕知此事要躬行。任何的實(shí)用技能都需要在不斷練習(xí)與使用中感悟、完善、提升, Spring Boot 也不例外。
所以還沒(méi)有使用 Spring Boot 的朋友,抓緊上手吧!