工作中這樣用MQ,很香!

👉 這是一個或許對你有用的社群
🐱 一對一交流/面試小冊/簡歷最佳化/求職解惑,歡迎加入芋道快速開發平臺知識星球。下面是星球提供的部分資料:
👉這是一個或許對你有用的開源專案
國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。
功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、ERPCRMAI 大模型等等功能:
  • Boot 多模組架構:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 微服務架構:https://gitee.com/zhijiantianya/yudao-cloud
  • 影片教程:https://doc.iocoder.cn
【國內首批】支援 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 雙版本 

前言

訊息佇列(MQ)是分散式系統中不可或缺的技術之一。
對很多小夥伴來說,剛接觸MQ時,可能覺得它只是個“傳話工具”,但用著用著,你會發現它簡直是系統的“潤滑劑”。
無論是解耦、削峰,還是非同步任務處理,都離不開MQ的身影。
下面我結合實際場景,從簡單到複雜,逐一拆解MQ的10種經典使用方式,希望對你會有所幫助。

基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 影片教程:https://doc.iocoder.cn/video/

1. 非同步處理:讓系統輕鬆一點

場景

小夥伴們是不是經常遇到這樣的情況:使用者提交一個操作,比如下單,然後要傳送簡訊通知。
如果直接在主流程裡呼叫簡訊介面,一旦簡訊服務響應慢,就會拖累整個操作。
使用者等得不耐煩,心態直接崩了。

解決方案

用MQ,把非關鍵流程抽出來非同步處理。下單時,直接把“發簡訊”這件事丟給MQ,訂單服務就能立刻響應使用者,而簡訊的事情讓MQ和消費者去搞定。

示例程式碼

// 訂單服務:生產者

Order order = createOrder(); 

// 訂單生成邏輯

rabbitTemplate.convertAndSend(

"order_exchange"

"order_key"

, order);

System.out.println(

"訂單已生成,發簡訊任務交給MQ"

);

// 簡訊服務:消費者
@RabbitListener

(queues = 

"sms_queue"

)

publicvoidsendSms(Order order)

{

    System.out.println(

"傳送簡訊,訂單ID:"

 + order.getId());

// 呼叫簡訊服務介面

}

深度解析

這種方式的好處是:主流程解耦,不受慢服務的拖累 。訂單服務只管自己的事,簡訊服務掛了也沒關係,MQ會把訊息暫存,等簡訊服務恢復後繼續處理。
基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
  • 專案地址:https://github.com/YunaiV/yudao-cloud
  • 影片教程:https://doc.iocoder.cn/video/

2. 流量削峰:穩住系統別崩

場景

每年的“雙十一”電商大促,使用者秒殺商品時一窩蜂衝進來。
突然湧入的高併發請求,不僅會壓垮應用服務,還會直接讓資料庫“趴窩”。

解決方案

秒殺請求先寫入MQ,後端服務以穩定的速度從MQ中消費訊息,處理訂單。
這樣既能避免系統被瞬時流量壓垮,還能提升處理的平穩性。

示例程式碼

// 使用者提交秒殺請求:生產者

rabbitTemplate.convertAndSend(

"seckill_exchange"

"seckill_key"

, userRequest);

System.out.println(

"使用者秒殺請求已進入佇列"

);

// 秒殺服務:消費者
@RabbitListener

(queues = 

"seckill_queue"

)

publicvoidprocessSeckill(UserRequest request)

{

    System.out.println(

"處理秒殺請求,使用者ID:"

 + request.getUserId());

// 執行秒殺邏輯

}

深度解析

MQ在這裡相當於一個緩衝池,把瞬時流量均勻分佈到一段時間內處理。系統穩定性提升,使用者體驗更好

3. 服務解耦:減少相互牽制

場景

比如一個訂單系統需要通知庫存系統扣減庫存,還要通知支付系統完成扣款。
如果直接用同步介面呼叫,服務間的依賴性很強,一個服務掛了,整個鏈條都會被拖垮。

解決方案

訂單服務只負責把訊息丟到MQ裡,庫存服務和支付服務各自從MQ中消費訊息。
這樣訂單服務不需要直接依賴它們。

示例程式碼

// 訂單服務:生產者

rabbitTemplate.convertAndSend(

"order_exchange"

"order_key"

, order);

System.out.println(

"訂單生成訊息已傳送"

);

// 庫存服務:消費者
@RabbitListener

(queues = 

"stock_queue"

)

publicvoidupdateStock(Order order)

{

    System.out.println(

"扣減庫存,訂單ID:"

 + order.getId());

}

// 支付服務:消費者
@RabbitListener

(queues = 

"payment_queue"

)

publicvoidprocessPayment(Order order)

{

    System.out.println(

"處理支付,訂單ID:"

 + order.getId());

}

深度解析

透過MQ,各個服務之間可以實現松耦合。
即使庫存服務掛了,也不會影響訂單生成的流程,大幅提升系統的容錯能力

4. 分散式事務:保證資料一致性

場景

訂單服務需要同時生成訂單和扣減庫存,這涉及兩個不同的資料庫操作。
如果一個成功一個失敗,就會導致資料不一致。

解決方案

透過MQ實現分散式事務。
訂單服務生成訂單後,將扣減庫存的任務交給MQ,最終實現資料的一致性。

示例程式碼

// 訂單服務:生產者

rabbitTemplate.convertAndSend(

"order_exchange"

"order_key"

, order);

System.out.println(

"訂單建立訊息已傳送"

);

// 庫存服務:消費者
@RabbitListener

(queues = 

"stock_queue"

)

publicvoidupdateStock(Order order)

{

    System.out.println(

"更新庫存,訂單ID:"

 + order.getId());

// 執行扣減庫存邏輯

}

深度解析

透過“最終一致性”解決了分散式事務的難題,雖然短時間內可能有資料不一致,但最終狀態一定是正確的。

5. 廣播通知:一條訊息,通知多個服務

場景

比如商品價格調整,庫存、搜尋、推薦服務都需要同步更新。
如果每個服務都要單獨通知,工作量會很大。

解決方案

MQ的廣播模式(Fanout)可以讓多個消費者訂閱同一條訊息,實現訊息的“一發多收”。

示例程式碼

// 生產者:廣播訊息

rabbitTemplate.convertAndSend(

"price_update_exchange"

""

, priceUpdate);

System.out.println(

"商品價格更新訊息已廣播"

);

// 消費者1:庫存服務
@RabbitListener

(queues = 

"stock_queue"

)

publicvoidupdateStockPrice(PriceUpdate priceUpdate)

{

    System.out.println(

"庫存價格更新:"

 + priceUpdate.getProductId());

}

// 消費者2:搜尋服務
@RabbitListener

(queues = 

"search_queue"

)

publicvoidupdateSearchPrice(PriceUpdate priceUpdate)

{

    System.out.println(

"搜尋價格更新:"

 + priceUpdate.getProductId());

}

深度解析

這種模式讓多個服務都能接收到同一條訊息,擴充套件性非常強

6. 日誌收集:分散式日誌集中化

場景

多個服務產生的日誌需要統一儲存和分析。
如果直接寫資料庫,可能導致效能瓶頸。

解決方案

各服務將日誌寫入MQ,日誌分析系統從MQ中消費訊息並統一處理。

示例程式碼

// 服務端:生產者

rabbitTemplate.convertAndSend(

"log_exchange"

"log_key"

, logEntry);

System.out.println(

"日誌已傳送"

);

// 日誌分析服務:消費者
@RabbitListener

(queues = 

"log_queue"

)

publicvoidprocessLog(LogEntry log)

{

    System.out.println(

"日誌處理:"

 + log.getMessage());

// 儲存或分析邏輯

}

7. 延遲任務:定時觸發操作

場景

使用者下單後,如果30分鐘內未支付,需要自動取消訂單。

解決方案

使用MQ的延遲佇列功能,設定訊息延遲消費的時間。

示例程式碼

// 生產者:傳送延遲訊息

rabbitTemplate.convertAndSend(

"delay_exchange"

"delay_key"

, order, message -> {

    message.getMessageProperties().setDelay(

30

 * 

60

 * 

1000

); 

// 延遲30分鐘
return

 message;

});

System.out.println(

"訂單取消任務已設定"

);

// 消費者:處理延遲訊息
@RabbitListener

(queues = 

"delay_queue"

)

publicvoidcancelOrder(Order order)

{

    System.out.println(

"取消訂單:"

 + order.getId());

// 取消訂單邏輯

}

8. 資料同步:跨系統保持資料一致

場景

在一個分散式系統中,多個服務依賴同一份資料來源。
例如,電商平臺的訂單狀態更新後,需要同步到快取系統和推薦系統。
如果讓每個服務直接從資料庫拉取資料,會增加資料庫壓力,還可能出現延遲或不一致的問題。

解決方案

利用MQ進行資料同步。訂單服務更新訂單狀態後,將更新資訊傳送到MQ,快取服務和推薦服務從MQ中消費訊息並同步資料。

示例程式碼

訂單服務:生產者
// 更新訂單狀態後,將訊息傳送到MQ

Order order = updateOrderStatus(orderId, 

"PAID"

); 

// 更新訂單狀態為已支付

rabbitTemplate.convertAndSend(

"order_exchange"

"order_status_key"

, order);

System.out.println(

"訂單狀態更新訊息已傳送:"

 + order.getId());

快取服務:消費者
@RabbitListener

(queues = 

"cache_update_queue"

)

publicvoidupdateCache(Order order)

{

    System.out.println(

"更新快取,訂單ID:"

 + order.getId() + 

" 狀態:"

 + order.getStatus());

// 更新快取邏輯

    cacheService.update(order.getId(), order.getStatus());

}

推薦服務:消費者
@RabbitListener

(queues = 

"recommendation_queue"

)

publicvoidupdateRecommendation(Order order)

{

    System.out.println(

"更新推薦系統,訂單ID:"

 + order.getId() + 

" 狀態:"

 + order.getStatus());

// 更新推薦服務邏輯

    recommendationService.updateOrderStatus(order);

}

深度解析

透過MQ實現資料同步的好處是:
  1. 減輕資料庫壓力 :避免多個服務同時查詢資料庫。
  2. 最終一致性 :即使某個服務處理延遲,MQ也能保障訊息不丟失,最終所有服務的資料狀態一致。

9. 分散式任務排程

場景

有些任務需要定時執行,比如每天凌晨清理過期訂單。
這些訂單可能分佈在多個服務中,如果每個服務獨立執行定時任務,可能會出現重複處理或任務遺漏的問題。

解決方案

使用MQ統一分發排程任務,每個服務根據自身的業務需求,從MQ中消費任務並執行。

示例程式碼

任務排程服務:生產者
// 定時任務生成器
@Scheduled

(cron = 

"0 0 0 * * ?"

// 每天凌晨觸發
publicvoidgenerateTasks()

{

    List<Task> expiredTasks = taskService.getExpiredTasks();

for

 (Task task : expiredTasks) {

        rabbitTemplate.convertAndSend(

"task_exchange"

"task_routing_key"

, task);

        System.out.println(

"任務已傳送:"

 + task.getId());

    }

}

訂單服務:消費者
@RabbitListener

(queues = 

"order_task_queue"

)

publicvoidprocessOrderTask(Task task)

{

    System.out.println(

"處理訂單任務:"

 + task.getId());

// 執行訂單清理邏輯

    orderService.cleanExpiredOrder(task);

}

庫存服務:消費者
@RabbitListener

(queues = 

"stock_task_queue"

)

publicvoidprocessStockTask(Task task)

{

    System.out.println(

"處理庫存任務:"

 + task.getId());

// 執行庫存釋放邏輯

    stockService.releaseStock(task);

}

深度解析

分散式任務排程可以解決:
  1. 重複執行 :每個服務只處理自己佇列中的任務。
  2. 任務遺漏 :MQ確保任務可靠傳遞,防止任務丟失。

10. 檔案處理:非同步執行大檔案任務

場景

使用者上傳一個大檔案後,需要對檔案進行處理(如格式轉換、壓縮等)並存儲。
如果同步執行這些任務,前端頁面可能會一直載入,導致使用者體驗差。

解決方案

使用者上傳檔案後,立即將任務寫入MQ,後臺非同步處理檔案,處理完成後通知使用者或更新狀態。

示例程式碼

上傳服務:生產者
// 上傳檔案後,將任務寫入MQ

FileTask fileTask = 

new

 FileTask();

fileTask.setFileId(fileId);

fileTask.setOperation(

"COMPRESS"

);

rabbitTemplate.convertAndSend(

"file_task_exchange"

"file_task_key"

, fileTask);

System.out.println(

"檔案處理任務已傳送,檔案ID:"

 + fileId);

檔案處理服務:消費者
@RabbitListener

(queues = 

"file_task_queue"

)

publicvoidprocessFileTask(FileTask fileTask)

{

    System.out.println(

"處理檔案任務:"

 + fileTask.getFileId() + 

" 操作:"

 + fileTask.getOperation());

// 模擬檔案處理邏輯
if

 (

"COMPRESS"

.equals(fileTask.getOperation())) {

        fileService.compressFile(fileTask.getFileId());

    } 

elseif

 (

"CONVERT"

.equals(fileTask.getOperation())) {

        fileService.convertFileFormat(fileTask.getFileId());

    }

// 更新任務狀態

    taskService.updateTaskStatus(fileTask.getFileId(), 

"COMPLETED"

);

}

前端輪詢或回撥通知
// 前端輪詢檔案處理狀態

setInterval(() => {

    fetch(`/file/status?fileId=${fileId}`)

        .then(response => response.json())

        .then(status => {

if

 (status === 

"COMPLETED"

) {

                alert(

"檔案處理完成!"

);

            }

        });

}, 

5000

);

深度解析

非同步檔案處理的優勢:
  1. 提升使用者體驗 :主執行緒迅速返回,減少使用者等待時間。
  2. 後臺任務靈活擴充套件 :支援多種操作邏輯,適應複雜檔案處理需求。

總結

訊息佇列不只是傳遞訊息的工具,更是系統解耦、提升穩定性和擴充套件性的利器。
在這10種經典場景中,每一種都能解決特定的業務痛點。

歡迎加入我的知識星球,全面提升技術能力。
👉 加入方式,長按”或“掃描”下方二維碼噢
星球的內容包括:專案實戰、面試招聘、原始碼解析、學習路線。
文章有幫助的話,在看,轉發吧。
謝謝支援喲 (*^__^*)

相關文章