處理任務“無需等待”:集成RabbitMQ實現異步通信與系統解耦

在前幾篇文章中,我們構建的Web應用遵循了一個常見的同步處理模式:用戶發出HTTP請求 -> Controller接收 -> Service處理(可能涉及數據庫操作、調用其他內部方法)-> Controller返回HTTP響應。這個流程簡單直接,但在某些場景下會遇到瓶頸:

  • 用戶體驗不佳:?如果Service層需要執行一些耗時操作(比如發送郵件/短信、生成復雜報表、調用外部慢API、進行大量計算),用戶就必須一直等待直到所有操作完成,才能收到響應。這會導致頁面卡頓,用戶體驗直線下降。

  • 系統耦合度高:?如果一個服務(比如訂單服務)需要通知另一個服務(比如庫存服務和通知服務),直接通過RPC或HTTP調用,會導致服務之間緊密耦合。如果被調用服務暫時不可用或處理緩慢,會直接影響調用方(訂單服務)的性能和可用性。

  • 流量洪峰處理能力差:?如果短時間內涌入大量請求(如秒殺活動),所有請求都直接沖擊后端服務和數據庫,很容易導致系統過載甚至崩潰。

如何解決這些問題?引入異步處理系統解耦是關鍵,而消息隊列 (MQ)?正是實現這兩者的利器。

想象一下去銀行辦理業務,如果每個柜員(服務)都必須等上一個客戶完全辦完所有流程(包括需要后臺審批的耗時環節)才接待下一個,效率會非常低。而引入叫號系統(消息隊列)后,你取號(發送消息),然后可以坐下等待(主流程結束),當柜員空閑時,會叫到你的號(消費消息)來處理你的業務。這大大提高了整體效率和用戶體驗。

讀完本文,你將學會:

  • 理解為什么需要消息隊列以及它的核心優勢。

  • 了解RabbitMQ的基本概念(生產者、消費者、隊列、交換機)。

  • 掌握如何使用spring-boot-starter-amqp輕松集成RabbitMQ。

  • 通過RabbitTemplate發送消息(簡單文本和Java對象)。

  • 使用@RabbitListener注解異步接收并處理消息。

  • (可選)了解如何通過Java配置聲明隊列、交換機和綁定。

準備好讓你的應用學會“異步分身術”,提升響應速度和系統韌性了嗎?

一、為什么需要消息隊列?核心優勢解析

消息隊列是一種提供異步通信機制的中間件。它允許不同的應用程序或服務通過發送和接收消息來進行通信,而無需直接相互連接。

核心優勢:

  1. 異步處理 (Asynchronous Processing):

    • 場景:?用戶注冊后需要發送歡迎郵件。

    • 同步方式:?保存用戶信息 -> 調用郵件發送接口 -> 等待郵件發送成功 -> 返回注冊成功響應給用戶。如果郵件接口慢,用戶注冊就會很慢。

    • 異步方式:?保存用戶信息 -> 發送一個“發送歡迎郵件”的消息到MQ ->?立即返回注冊成功響應給用戶。后臺有一個獨立的郵件服務會從MQ消費這個消息并執行發送操作。

    • 效果:?用戶注冊響應速度大大提升。

  2. 應用解耦 (Decoupling):

    • 場景:?訂單創建成功后,需要通知庫存服務扣減庫存、通知物流服務準備發貨、通知積分服務增加積分。

    • 緊耦合方式:?訂單服務依次調用庫存、物流、積分服務的接口。任何一個下游服務接口變更或不可用,都會影響訂單服務。

    • MQ方式:?訂單服務只需要發送一個“訂單已創建”的消息(包含訂單信息)到MQ。庫存、物流、積分服務各自訂閱這個消息,獨立進行處理。

    • 效果:?訂單服務不再強依賴下游服務,下游服務增減或變更對訂單服務透明。系統更靈活、易于擴展。

  3. 削峰填谷 (Traffic Shaping / Load Leveling):

    • 場景:?秒殺活動開始瞬間,大量下單請求涌入。

    • 直接處理:?所有請求直接打到訂單服務和數據庫,很容易超出處理能力導致系統崩潰。

    • MQ方式:?前端應用或網關快速接收請求,將下單請求轉化為消息放入MQ。后端的訂單處理服務按照自己的節奏(比如每秒處理100個)從MQ中拉取消息進行處理。

    • 效果:?MQ作為緩沖區,平滑了流量洪峰,保護了后端系統不被打垮,保證了系統的穩定性。

二、初識RabbitMQ:核心概念速覽

RabbitMQ是一個實現了AMQP(高級消息隊列協議)的、流行的、開源的消息代理(Message Broker)。理解以下幾個核心概念對于使用它至關重要:

  • Producer (生產者):?發送消息的應用程序。

  • Consumer (消費者):?接收并處理消息的應用程序。

  • Broker (代理):?RabbitMQ服務器本身,負責接收、存儲和路由消息。

  • Queue (隊列):?消息存儲的緩沖區,位于Broker內部。消息從生產者發出后,最終被路由到隊列中等待消費者處理。多個消費者可以監聽同一個隊列(但一條消息通常只會被一個消費者處理 - P2P模式)。

  • Exchange (交換機):?接收來自生產者的消息,并根據路由規則 (Routing Key)?將消息路由到一個或多個隊列。生產者實際上是將消息發送到Exchange。Exchange有幾種類型,決定了路由邏輯:

    • Direct Exchange:?根據Routing Key精確匹配,將消息路由到Binding Key與之完全相同的隊列。

    • Fanout Exchange:?忽略Routing Key,將消息廣播到所有綁定到它的隊列。

    • Topic Exchange:?根據Routing Key進行模式匹配(使用?*?匹配一個單詞,#?匹配零個或多個單詞),將消息路由到匹配模式的隊列。

    • Headers Exchange:?根據消息頭中的屬性進行匹配(不常用)。

  • Binding (綁定):?定義Exchange和Queue之間的連接關系。對于Direct和Topic Exchange,Binding通常還包含一個Binding Key,用于匹配消息的Routing Key。

  • Message (消息):?生產者和消費者之間傳遞的數據。通常包含兩部分:Payload (消息體)?和?Headers (消息頭,可選的屬性)

簡化流程 (以Direct Exchange為例):
Producer -(消息 + Routing Key A)-> Exchange -(Binding Key A)-> Queue A <- Consumer A
Producer -(消息 + Routing Key B)-> Exchange -(Binding Key B)-> Queue B <- Consumer B

三、Spring Boot集成:spring-boot-starter-amqp

Spring Boot通過spring-boot-starter-amqp模塊極大地簡化了與RabbitMQ(以及其他AMQP兼容的Broker)的集成。

1. 添加依賴 (Maven):

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 配置連接信息 (application.yml):

spring:rabbitmq:host: localhost       # RabbitMQ服務器地址 (默認localhost)port: 5672            # RabbitMQ端口 (默認5672)username: guest       # 用戶名 (默認guest)password: guest       # 密碼 (默認guest)# virtual-host: /     # 虛擬主機 (默認/)# publisher-confirm-type: correlated # (可選) 開啟發送方確認模式# publisher-returns: true            # (可選) 開啟發送失敗退回模式# template:#   mandatory: true                # (可選) 配合publisher-returns, 確保消息至少路由到一個隊列

注意:?生產環境中,用戶名和密碼應使用上一篇文章介紹的配置管理方式(如環境變量、外部文件)注入,而非硬編碼。

配置完成后,Spring Boot會自動配置好連接工廠 (ConnectionFactory)、管理模板 (RabbitAdmin) 以及發送消息的核心工具?RabbitTemplate。

四、發送消息 (Producer):?RabbitTemplate

RabbitTemplate是Spring AMQP提供的用于發送消息的核心類。

示例:用戶注冊后異步發送歡迎郵件通知

  1. 修改UserService?(注入RabbitTemplate):

    package com.example.service;import com.example.model.User;
    import com.example.repository.UserRepository;
    import org.springframework.amqp.rabbit.core.RabbitTemplate; // 導入
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;@Service
    public class UserService {private final UserRepository userRepository;private final RabbitTemplate rabbitTemplate; // 注入RabbitTemplate// 定義隊列名稱 (最好定義為常量或配置)public static final String WELCOME_EMAIL_QUEUE = "q.user.welcome.email";// 定義交換機名稱 (使用默認Direct交換機時為空字符串, RoutingKey就是QueueName)public static final String DEFAULT_EXCHANGE = ""; // 空字符串代表默認交換機@Autowiredpublic UserService(UserRepository userRepository, RabbitTemplate rabbitTemplate) {this.userRepository = userRepository;this.rabbitTemplate = rabbitTemplate;}@Transactionalpublic User createUser(String name, String email, Integer age) {User newUser = new User(name, email, age);User savedUser = userRepository.save(newUser);System.out.println("Saved user to DB: " + savedUser);// --- 異步發送消息 ---try {// 發送消息到指定隊列 (使用默認交換機, routingKey就是隊列名)// convertAndSend 會自動將 User 對象序列化 (通常為JSON)rabbitTemplate.convertAndSend(DEFAULT_EXCHANGE, WELCOME_EMAIL_QUEUE, savedUser);System.out.println("Sent welcome email task message for user: " + savedUser.getEmail());// 你也可以只發送必要的ID或信息, 而不是整個對象// rabbitTemplate.convertAndSend(WELCOME_EMAIL_QUEUE, savedUser.getId());} catch (Exception e) {// 考慮: 消息發送失敗的處理策略 (記錄日志, 補償任務等)System.err.println("Failed to send welcome email task message: " + e.getMessage());// 注意: 這里不應影響主事務的回滾 (如果需要的話)}return savedUser;}// ... 其他方法 ...
    }

    convertAndSend(String exchange, String routingKey, Object message)?是最常用的發送方法。它會自動處理對象到消息的轉換(默認使用Jackson2JsonMessageConverter轉為JSON)。

五、接收消息 (Consumer):?@RabbitListener

通過@RabbitListener注解,可以非常方便地創建消息消費者。

示例:創建郵件服務消費者來處理歡迎郵件任務

  1. 創建EmailConsumer組件:

    package com.example.consumer;import com.example.model.User; // 需要能訪問User類
    import org.springframework.amqp.rabbit.annotation.RabbitListener; // 導入
    import org.springframework.stereotype.Component;@Component
    public class EmailConsumer {// 使用 @RabbitListener 注解監聽指定隊列// Spring AMQP 會自動創建隊列 (如果不存在且配置允許)@RabbitListener(queues = UserService.WELCOME_EMAIL_QUEUE)public void handleWelcomeEmail(User user) { // 參數類型與發送時一致 (或Object/Message)System.out.println("Received welcome email task for user: " + user);try {// --- 模擬發送郵件的耗時操作 ---System.out.println("Simulating sending welcome email to " + user.getEmail() + "...");Thread.sleep(2000); // 模擬耗時2秒System.out.println("Welcome email sent successfully to " + user.getEmail());// 如果處理成功, Spring AMQP 會自動發送 ACK (消息確認) 給RabbitMQ// RabbitMQ 確認后會從隊列中刪除該消息} catch (InterruptedException e) {Thread.currentThread().interrupt();System.err.println("Email sending task interrupted for user: " + user.getEmail());// 拋出異常會導致消息處理失敗throw new RuntimeException("Email sending interrupted", e);} catch (Exception e) {// 其他異常也可能導致處理失敗System.err.println("Error sending welcome email to " + user.getEmail() + ": " + e.getMessage());// 如果方法拋出異常, Spring AMQP 默認會拒絕消息 (NACK)// 根據配置, 消息可能會被重新入隊 (可能導致死循環!) 或進入死信隊列 (推薦)throw e; // 重新拋出, 讓Spring AMQP知道處理失敗}}// 可以監聽同一個隊列的多個實例 (用于提高并發處理能力)// @RabbitListener(queues = UserService.WELCOME_EMAIL_QUEUE)// public void handleWelcomeEmailInstance2(User user) { ... }// 監聽其他隊列// @RabbitListener(queues = "another.queue")// public void handleAnotherTask(String messagePayload) { ... }
    }

    • @RabbitListener(queues = "..."): 指定要監聽的隊列名稱。

    • 方法參數可以直接是消息體反序列化后的對象類型(如User)。Spring AMQP會自動完成轉換。也可以是org.springframework.amqp.core.Message獲取完整消息,或com.rabbitmq.client.Channel進行手動ACK等高級操作。

    • 消息確認 (Acknowledgement, ACK):?默認情況下,如果@RabbitListener方法成功執行完畢(沒有拋出異常),Spring AMQP會自動向RabbitMQ發送ACK,告知消息已被成功處理,可以從隊列中刪除了。如果方法拋出異常,則會發送NACK(或Reject),消息可能會被重新投遞或進入死信隊列(需要額外配置)。這是保證消息不丟失的關鍵機制。

六、最佳實踐:聲明式定義基礎設施

雖然RabbitTemplate和@RabbitListener在某些配置下可以自動創建隊列,但在生產環境中,推薦顯式地聲明所需的隊列、交換機和綁定。這能確保基礎設施的存在,避免因自動創建的不可靠性導致問題,并且使配置更清晰。

可以通過在@Configuration類中定義Queue,?Exchange,?Binding類型的Bean來實現:

package com.example.config;import org.springframework.amqp.core.*; // 導入核心類
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitMQConfig {// --- 聲明歡迎郵件隊列 ---@Beanpublic Queue welcomeEmailQueue() {// durable(true) 持久化隊列 (RabbitMQ重啟后依然存在)return new Queue(UserService.WELCOME_EMAIL_QUEUE, true);}// --- (可選) 如果不使用默認交換機, 可以聲明一個交換機 ---// 例如, 聲明一個 Direct Exchange// @Bean// public DirectExchange userEventsExchange() {//     return new DirectExchange("x.user.events", true, false);// }// --- (可選) 聲明綁定關系 ---// 將歡迎郵件隊列綁定到默認交換機 (RoutingKey就是隊列名)@Beanpublic Binding welcomeEmailBinding(Queue welcomeEmailQueue) {// 目標 (隊列), 類型 (隊列), 交換機 (默認), RoutingKey, 參數return BindingBuilder.bind(welcomeEmailQueue).to(DirectExchange.DEFAULT).withQueueName();}// 如果使用了自定義交換機:// @Bean// public Binding welcomeEmailBindingToUserExchange(Queue welcomeEmailQueue, DirectExchange userEventsExchange) {//    return BindingBuilder.bind(welcomeEmailQueue).to(userEventsExchange).with(UserService.WELCOME_EMAIL_QUEUE); // 使用隊列名作為RoutingKey// }// ---- 可以聲明其他隊列、交換機和綁定 ----// @Bean public Queue orderCreatedQueue() { ... }// @Bean public FanoutExchange notificationExchange() { ... }// @Bean public Binding orderNotificationBinding(Queue orderCreatedQueue, FanoutExchange notificationExchange) { ... }
}

Spring AMQP啟動時會檢查這些Bean,如果對應的隊列、交換機或綁定在RabbitMQ中不存在,RabbitAdmin會自動創建它們。

七、何時使用消息隊列?

  • 需要將耗時操作從主流程中剝離,提高用戶響應速度時(如郵件發送、報表生成)。

  • 需要解耦不同服務或模塊之間的依賴關系時(如訂單與庫存、物流、積分)。

  • 需要緩沖突發流量,保護后端系統時(如秒殺、批量數據導入)。

  • 構建事件驅動架構時。

八、總結:開啟異步與解耦的新篇章

消息隊列(如RabbitMQ)是構建健壯、可擴展的現代分布式系統的重要工具。通過引入異步處理和應用解耦,它可以顯著提升用戶體驗、系統靈活性和穩定性。Spring Boot AMQP (spring-boot-starter-amqp) 提供了與RabbitMQ無縫集成的能力,通過RabbitTemplate發送消息和@RabbitListener消費消息,使得在Spring應用中使用MQ變得異常簡單。

掌握消息隊列的集成與使用,將為你的應用程序架構設計打開新的思路,助你構建更加高效、可靠的系統。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/78201.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78201.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/78201.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Obsidian和Ollama大語言模型的交互過程

之前的文章中介紹了Obsidian配合Ollama的使用案例&#xff0c;那么它們是如何配合起來的呢&#xff1f;其實這個問題并不準確&#xff0c;問題的準確描述應該是Obsidian的Copilot插件是如何與Ollama大語言模型交互的。因為Obsidian在這里只是一個載體&#xff0c;核心功能還是C…

4.1 融合架構設計:LLM與Agent的協同工作模型

大型語言模型&#xff08;Large Language Models, LLMs&#xff09;與智能代理&#xff08;Agent&#xff09;的融合架構已成為人工智能領域推動企業智能化的核心技術。這種協同工作模型利用LLM的語言理解、推理和生成能力&#xff0c;為Agent提供強大的知識支持&#xff0c;而…

龍虎榜——20250424

指數依然是震蕩走勢&#xff0c;接下來兩天調整的概率較大 2025年4月24日龍虎榜行業方向分析 一、核心主線方向 化工&#xff08;新能源材料產能集中&#xff09; ? 代表標的&#xff1a;紅寶麗&#xff08;環氧丙烷/鋰電材料&#xff09;、中欣氟材&#xff08;氟化工&…

Linux 服務器運維常用命令大全

1.基礎命令 1.1 文件與目錄操作 ls -l #列出文件詳細信息 ls -a #顯示隱藏文件 cd /path/to/directory #切換目錄 pwd #顯示當前工作目錄 mkdir dirname #創建目錄 rm -rf dirname #刪除…

動態渲染頁面智能嗅探:機器學習判定AJAX加載觸發條件

本文提出了一種基于機器學習的智能嗅探機制&#xff0c;革新性地應用于自動判定動態渲染頁面中AJAX加載的最佳觸發時機。系統架構采用先進模塊化拆解設計&#xff0c;由請求分析模塊、機器學習判定模塊、數據采集模塊和文件存儲模塊四大核心部分構成。在核心代碼示例中&#xf…

sql高級之回表

避免回表是數據庫查詢優化的核心目標之一&#xff0c;指通過索引直接獲取查詢所需的全部數據&#xff0c;無需根據索引結果再回主表&#xff08;數據行&#xff09;讀取其他字段&#xff0c;從而減少磁盤 I/O 和計算開銷。以下是詳細解釋&#xff1a; 1. 什么是回表&#xff1…

第十一屆機械工程、材料和自動化技術國際會議(MMEAT 2025)

重要信息 官網&#xff1a;www.mmeat.net 時間&#xff1a;2025年06月23-25日 地點&#xff1a;中國-深圳 部分展示 征稿主題 智能制造和工業自動化 復合材料與高性能材料先進制造技術 自動化機器人系統 云制造與物聯網集成 精密制造技術 智能生產線優化 實時數據分析與過…

動態自適應分區算法(DAPS)設計流程詳解

動態自適應分區算法&#xff08;Dynamic Adaptive Partitioning System, DAPS&#xff09;是一種通過實時監測系統狀態并動態調整資源分配策略的智能算法&#xff0c;廣泛應用于緩存優化、分布式系統、工業制造等領域。本文將從設計流程的核心步驟出發&#xff0c;結合數學模型…

從入門到精通:CMakeLists.txt 完全指南

從入門到精通&#xff1a;CMakeLists.txt 完全指南 CMake 是一個跨平臺的自動化構建系統&#xff0c;它使用名為 CMakeLists.txt 的配置文件來控制軟件的編譯過程。無論你是剛接觸 CMake 的新手&#xff0c;還是希望提升 CMake 技能的中級開發者&#xff0c;這篇指南都將帶你從…

CPT204 Advanced Obejct-Oriented Programming 高級面向對象編程 Pt.8 排序算法

文章目錄 1. 排序算法1.1 冒泡排序&#xff08;Bubble sort&#xff09;1.2 歸并排序&#xff08;Merge Sort&#xff09;1.3 快速排序&#xff08;Quick Sort&#xff09;1.4 堆排序&#xff08;Heap Sort&#xff09; 2. 在面向對象編程中終身學習2.1 記錄和反思學習過程2.2 …

【element plus】解決報錯error:ResizeObserver loop limit exceeded的問題

當我們在使用element plus框架時&#xff0c;有時會遇到屏幕突然變暗&#xff0c;然后來一句莫名其妙的報錯ResizeObserver loop limit exceeded&#xff0c;其實這是因為改變屏幕大小時el-table導致的報錯 網上給出了幾種解決方案&#xff0c;我試了其中兩種可以實現 方案一&…

LeetCode算法題(Go語言實現)_60

題目 給你一個整數數組 cost &#xff0c;其中 cost[i] 是從樓梯第 i 個臺階向上爬需要支付的費用。一旦你支付此費用&#xff0c;即可選擇向上爬一個或者兩個臺階。 你可以選擇從下標為 0 或下標為 1 的臺階開始爬樓梯。 請你計算并返回達到樓梯頂部的最低花費。 一、代碼實現…

馬架構的Netty、MQTT、CoAP面試之旅

標題&#xff1a;馬架構的Netty、MQTT、CoAP面試之旅 在互聯網大廠的Java求職者面試中&#xff0c;一位名叫馬架構的資深Java架構師正接受著嚴格的考驗。他擁有十年的Java研發經驗和架構設計經驗&#xff0c;尤其對疑難問題和線索問題等有著豐富的經歷。 第一輪提問&#xff…

焦化燒結行業無功補償解決方案—精準分組補償 穩定電能質量沃倫森

在焦化、燒結等冶金行業&#xff0c;負荷運行呈現長時階梯狀變化&#xff0c;功率波動相對平緩&#xff0c;但對無功補償的分組精度要求較高。傳統固定電容器組補償方式無法動態跟隨負荷變化&#xff0c;導致功率因數不穩定&#xff0c;甚至可能因諧波放大影響電網安全。 行業…

使用String path = FileUtilTest.class.getResource(“/1.txt“).getPath(); 報找不到路徑

在windows環境運行&#xff0c;下面的springboot中path怎么找不到文件呢&#xff1f; path輸出后的結果是&#xff1a;路徑是多少&#xff1a;/D:/bjpowernode/msb/%e4%b9%90%e4%b9%8b%e8%80%85/apache%20commons/SpringBootBase6/target/test-classes/1.txt 怎么解決一下呢&am…

【C++】二叉樹進階面試題

根據二叉樹創建字符串 重點是要注意括號省略問題&#xff0c;分為以下情況&#xff1a; 1.左字樹為空&#xff0c;右子樹不為空&#xff0c;左邊括號保留 2.左右子樹都為空&#xff0c;括號都不保留 3。左子樹不為空&#xff0c;右子樹為空&#xff0c;右邊括號不保留 如果根節…

RSUniVLM論文精讀

一些收獲&#xff1a; 1. 發現這篇文章的table1中&#xff0c;有CDChat ChangeChat Change-Agent等模型&#xff0c;也許用得上。等會看看有沒有源代碼。 摘要&#xff1a;RSVLMs在遙感圖像理解任務中取得了很大的進展。盡管在多模態推理和多輪對話中表現良好&#xff0c;現有模…

低空AI系統的合規化與標準化演進路徑

隨著AI無人機集群逐步參與城市空域治理、物流服務與公共安全作業&#xff0c;其系統行為不再是“技術封閉域”&#xff0c;而需接受法規監管、責任評估與接口協同的多方審查。如何將AI集群系統推向標準化、可接入、可審計的合規體系&#xff0c;成為未來空中交通演進的關鍵。本…

【金倉數據庫征文】從云計算到區塊鏈:金倉數據庫的顛覆性創新之路

目錄 一、引言 二、金倉數據庫概述 2.1 金倉數據庫的背景 2.2 核心技術特點 2.3 行業應用案例 三、金倉數據庫的產品優化提案 3.1 性能優化 3.1.1 查詢優化 3.1.2 索引優化 3.1.3 緩存優化 3.2 可擴展性優化 3.2.1 水平擴展與分區設計 3.2.2 負載均衡與讀寫分離 …

致遠oa部署

文章目錄 環境搭建項目構建 僅供學習使用 環境搭建 準備項目&#xff1a; https://pan.quark.cn/s/04a166575e94 https://pan.xunlei.com/s/VOOc1c9dBdLIuU8KKiqDa68NA1?pwdmybd# 官方文檔: https://open.seeyoncloud.com/v5devCTP/ 安裝時 mysql 數據庫可能出現字符集設置…