WebSocket深度指南:從零基礎到生產級應用

??目錄

1. WebSocket基礎概念深度解析

2. WebSocket協議技術詳解

3. WebSocket生命周期與狀態管理

4. Spring Boot WebSocket完整實現

5. 完整聊天室項目實戰

6. 高級功能與擴展應用


1. WebSocket基礎概念深度解析

1.1 什么是WebSocket?深度理解

WebSocket是HTML5開始提供的一種在單個TCP連接上進行全雙工通信的協議。

?? 核心理念對比:

傳統HTTP模式(請求-響應):

客戶端 ----請求----> 服務器
客戶端 <----響應---- 服務器
[連接關閉,下次需要重新建立]優點:簡單、無狀態、易于理解
缺點:無法主動推送、開銷大、延遲高

WebSocket模式(持久連接):

客戶端 ====握手====> 服務器
客戶端 <====雙向通信====> 服務器
[連接保持開放狀態]優點:實時雙向、低延遲、低開銷
缺點:復雜性增加、狀態管理需要

1.2 WebSocket解決的核心問題

1.2.1 實時性問題

傳統解決方案的局限:

// 1. 輪詢 (Polling) - 浪費資源
setInterval(() => {fetch('/api/check-updates').then(response => response.json()).then(data => {if (data.hasUpdates) {updateUI(data);}});
}, 1000); // 每秒請求一次,即使沒有更新// 2. 長輪詢 (Long Polling) - 復雜且不穩定
function longPoll() {fetch('/api/long-poll').then(response => response.json()).then(data => {updateUI(data);longPoll(); // 遞歸調用}).catch(() => {setTimeout(longPoll, 5000); // 錯誤后重試});
}// 3. Server-Sent Events (SSE) - 單向通信
const eventSource = new EventSource('/api/events');
eventSource.onmessage = (event) => {updateUI(JSON.parse(event.data));
};
// 只能服務器向客戶端推送,客戶端無法主動發送

WebSocket的優勢:

// WebSocket - 真正的雙向實時通信
const ws = new WebSocket('ws://localhost:8080/realtime');// 立即接收服務器推送
ws.onmessage = (event) => {updateUI(JSON.parse(event.data));
};// 客戶端主動發送
ws.send(JSON.stringify({type: 'user_action',data: 'some_data'
}));
1.2.2 網絡開銷問題

HTTP請求開銷分析:

GET /api/data HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0...
Accept: application/json
Cookie: session=abc123...
Authorization: Bearer token...
Cache-Control: no-cache
Connection: close[總共約800-1500字節的頭部信息,僅為獲取可能只有幾字節的數據]

WebSocket幀開銷:

WebSocket最小幀:2字節頭部 + 數據
相比HTTP減少95%以上的開銷

1.3 WebSocket技術特性詳解

1.3.1 全雙工通信
// 客戶端可以隨時發送消息
ws.send('Hello from client at ' + new Date());// 服務器也可以隨時推送消息
// 服務器端代碼會在后面詳細講解
1.3.2 協議升級機制

WebSocket通過HTTP升級機制建立連接:

# 第1步:客戶端發起升級請求
GET /chat HTTP/1.1
Host: localhost:8080
Upgrade: websocket                    # 要求升級到WebSocket
Connection: Upgrade                   # 連接升級
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==    # 安全密鑰
Sec-WebSocket-Version: 13            # WebSocket版本
Sec-WebSocket-Protocol: chat, superchat        # 可選子協議
Sec-WebSocket-Extensions: permessage-deflate   # 可選擴展# 第2步:服務器響應升級
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=  # 基于客戶端密鑰計算的接受密鑰
Sec-WebSocket-Protocol: chat                          # 選擇的子協議# 第3步:協議切換完成,開始WebSocket通信
1.3.3 子協議和擴展
// 指定子協議
const ws = new WebSocket('ws://localhost:8080/chat', ['chat-v1', 'chat-v2']);// 檢查服務器選擇的協議
ws.onopen = () => {console.log('使用的協議:', ws.protocol);
};

2. WebSocket協議技術詳解

2.1 數據幀結構深度分析

WebSocket使用幀(Frame)進行數據傳輸,每個幀包含以下信息:

 0                   1                   2                   30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

詳細字段解釋:

字段位數說明
FIN1指示這是否是消息的最后一個片段
RSV1-33保留位,必須為0(除非擴展定義了非零值)
Opcode4操作碼,定義幀的類型
MASK1指示載荷數據是否被掩碼(客戶端到服務器必須為1)
Payload Length7載荷數據長度
Extended Payload Length16/64擴展載荷長度(當基礎長度為126或127時)
Masking Key32掩碼密鑰(當MASK=1時存在)
Payload Data變長實際傳輸的數據

2.2 操作碼詳解

操作碼描述用途
Continuation0x0繼續幀分片消息的后續幀
Text0x1文本幀UTF-8編碼的文本數據
Binary0x2二進制幀二進制數據
Close0x8關閉幀關閉連接
Ping0x9Ping幀心跳檢測
Pong0xAPong幀對Ping的響應

2.3 消息分片機制

大消息可以分割成多個幀傳輸:

// 發送大文件的示例
function sendLargeFile(file) {const chunkSize = 1024 * 64; // 64KB per chunkconst totalChunks = Math.ceil(file.size / chunkSize);for (let i = 0; i < totalChunks; i++) {const start = i * chunkSize;const end = Math.min(start + chunkSize, file.size);const chunk = file.slice(start, end);const message = {type: 'file_chunk',chunkIndex: i,totalChunks: totalChunks,fileName: file.name,data: chunk};ws.send(JSON.stringify(message));}
}

2.4 掩碼機制

客戶端發送的所有幀都必須使用掩碼,防止緩存污染攻擊:

// 掩碼算法(JavaScript示例,實際由瀏覽器自動處理)
function maskData(data, maskKey) {const masked = new Uint8Array(data.length);for (let i = 0; i < data.length; i++) {masked[i] = data[i] ^ maskKey[i % 4];}return masked;
}

3. WebSocket生命周期與狀態管理

3.1 連接狀態詳解

WebSocket連接有四個狀態:

const WebSocketState = {CONNECTING: 0,  // 正在連接OPEN: 1,        // 連接已建立CLOSING: 2,     // 連接正在關閉CLOSED: 3       // 連接已關閉
};// 檢查連接狀態
function checkWebSocketState(ws) {switch(ws.readyState) {case WebSocket.CONNECTING:console.log('正在連接到服務器...');break;case WebSocket.OPEN:console.log('連接已建立,可以發送數據');break;case WebSocket.CLOSING:console.log('連接正在關閉...');break;case WebSocket.CLOSED:console.log('連接已關閉');break;}
}

3.2 完整的生命周期管理

class WebSocketManager {constructor(url, options = {}) {this.url = url;this.options = {reconnectInterval: 1000,maxReconnectAttempts: 5,heartbeatInterval: 30000,...options};this.ws = null;this.reconnectAttempts = 0;this.heartbeatTimer = null;this.reconnectTimer = null;this.listeners = {open: [],message: [],close: [],error: []};}connect() {try {this.ws = new WebSocket(this.url);this.setupEventHandlers();} catch (error) {console.error('WebSocket連接失敗:', error);this.handleReconnect();}}setupEventHandlers() {this.ws.onopen = (event) => {console.log('WebSocket連接已建立');this.reconnectAttempts = 0;this.startHeartbeat();this.emit('open', event);};this.ws.onmessage = (event) => {console.log('收到消息:', event.data);// 處理心跳響應if (event.data === 'pong') {console.log('收到心跳響應');return;}this.emit('message', event);};this.ws.onclose = (event) => {console.log('WebSocket連接已關閉:', event.code, event.reason);this.stopHeartbeat();this.emit('close', event);// 非正常關閉時嘗試重連if (event.code !== 1000) {this.handleReconnect();}};this.ws.onerror = (error) => {console.error('WebSocket錯誤:', error);this.emit('error', error);};}send(data) {if (this.ws && this.ws.readyState === WebSocket.OPEN) {this.ws.send(typeof data === 'string' ? data : JSON.stringify(data));return true;} else {console.warn('WebSocket未連接,無法發送數據');return false;}}close(code = 1000, reason = 'Normal closure') {if (this.ws) {this.ws.close(code, reason);}this.stopHeartbeat();this.stopReconnect();}// 心跳機制startHeartbeat() {this.heartbeatTimer = setInterval(() => {if (this.ws && this.ws.readyState === WebSocket.OPEN) {this.ws.send('ping');}}, this.options.heartbeatInterval);}stopHeartbeat() {if (this.heartbeatTimer) {clearInterval(this.heartbeatTimer);this.heartbeatTimer = null;}}// 重連機制handleReconnect() {if (this.reconnectAttempts < this.options.maxReconnectAttempts) {this.reconnectAttempts++;const delay = this.options.reconnectInterval * Math.pow(2, this.reconnectAttempts - 1);console.log(`${delay}ms后嘗試第${this.reconnectAttempts}次重連`);this.reconnectTimer = setTimeout(() => {this.connect();}, delay);} else {console.error('達到最大重連次數,停止重連');}}stopReconnect() {if (this.reconnectTimer) {clearTimeout(this.reconnectTimer);this.reconnectTimer = null;}}// 事件監聽器on(event, callback) {if (this.listeners[event]) {this.listeners[event].push(callback);}}emit(event, data) {if (this.listeners[event]) {this.listeners[event].forEach(callback => callback(data));}}
}// 使用示例
const wsManager = new WebSocketManager('ws://localhost:8080/chat', {reconnectInterval: 2000,maxReconnectAttempts: 10,heartbeatInterval: 25000
});wsManager.on('open', () => {console.log('連接成功!');
});wsManager.on('message', (event) => {const data = JSON.parse(event.data);handleMessage(data);
});wsManager.connect();

3.3 關閉代碼詳解

WebSocket關閉時會返回狀態碼:

代碼名稱描述
1000Normal Closure正常關閉
1001Going Away端點離開(如頁面關閉)
1002Protocol Error協議錯誤
1003Unsupported Data不支持的數據類型
1004Reserved保留
1005No Status Received未收到狀態碼
1006Abnormal Closure異常關閉
1007Invalid Frame Payload Data無效的幀載荷數據
1008Policy Violation違反策略
1009Message Too Big消息過大
1010Mandatory Extension強制擴展
1011Internal Error內部錯誤
1015TLS HandshakeTLS握手失敗
ws.onclose = (event) => {switch(event.code) {case 1000:console.log('正常關閉');break;case 1001:console.log('頁面離開或服務器關閉');break;case 1006:console.log('連接異常斷開,可能需要重連');break;default:console.log(`連接關閉,代碼: ${event.code}, 原因: ${event.reason}`);}
};

4. Spring Boot WebSocket完整實現

4.1 項目結構與依賴

4.1.1 完整的項目結構
websocket-chat/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── websocketchat/
│   │   │               ├── WebSocketChatApplication.java
│   │   │               ├── config/
│   │   │               │   ├── WebSocketConfig.java
│   │   │               │   ├── WebSocketStompConfig.java
│   │   │               │   └── WebSocketSecurityConfig.java
│   │   │               ├── controller/
│   │   │               │   ├── ChatController.java
│   │   │               │   └── WebController.java
│   │   │               ├── handler/
│   │   │               │   └── ChatWebSocketHandler.java
│   │   │               ├── model/
│   │   │               │   ├── ChatMessage.java
│   │   │               │   ├── ChatUser.java
│   │   │               │   └── MessageType.java
│   │   │               ├── service/
│   │   │               │   ├── ChatService.java
│   │   │               │   ├── UserSessionService.java
│   │   │               │   └── MessageService.java
│   │   │               ├── listener/
│   │   │               │   └── WebSocketEventListener.java
│   │   │               └── util/
│   │   │                   └── WebSocketSessionManager.java
│   │   └── resources/
│   │       ├── static/
│   │       │   ├── css/
│   │       │   │   └── chat.css
│   │       │   ├── js/
│   │       │   │   ├── chat.js
│   │       │   │   └── websocket-manager.js
│   │       │   └── index.html
│   │       └── application.yml
│   └── test/
└── pom.xml
4.1.2 詳細的Maven依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>websocket-chat</artifactId><version>1.0.0</version><name>websocket-chat</name><description>WebSocket聊天室完整項目</description><properties><java.version>17</java.version></properties><dependencies><!-- Spring Boot WebSocket啟動器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- Spring Boot Web啟動器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot安全啟動器(可選) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><optional>true</optional></dependency><!-- Spring Boot數據JPA(可選,用于消息持久化) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId><optional>true</optional></dependency><!-- H2數據庫(開發測試用) --><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope><optional>true</optional></dependency><!-- JSON處理 --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId></dependency><!-- 日志處理 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><!-- 開發工具 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><!-- 測試依賴 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

4.2 配置文件詳解

4.2.1 application.yml配置
# application.yml
server:port: 8080servlet:context-path: /spring:application:name: websocket-chat# WebSocket相關配置we

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

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

相關文章

復現 apache HTTPD 換行解析漏洞(CVE-2017-15715)

一、漏洞環境 docker環境 http://192.168.99.124:8082二、漏洞原理 Apache HTTPD是一款HTTP服務器&#xff0c;它可以通過mod_php來運行PHP網頁。其2.4.0~2.4.29版本中存在一個解析漏洞&#xff0c;在解析PHP時&#xff0c;1.php\x0A將被按照PHP后綴進行解析&#xff0c;導致…

創始人 IP 起盤方法論:從 0 到 1 的系統化破局路徑

在流量邏輯不斷更新的當下&#xff0c;創始人 IP 如何構建可持續的商業閉環&#xff1f;結合行業頭部案例的實戰經驗&#xff0c;可梳理出一套兼顧落地性與前瞻性的起盤策略&#xff0c;幫助 IP 在波動的市場中建立穩定的變現能力。 一、定位&#xff1a;在動態中驗證方向 某…

數據結構 6(算法)

一、算法 1、概念 問題的求解方法 2、算法的特性和設計要求 算法的特性&#xff1a; 確定性 有窮性 輸入輸出 可行性 設計要求&#xff1a; 正確性 高效性 低存儲 健壯性 可讀性 3、時間復雜度O(n) 用于評估程序執行…

Android 開發問題:android.content.res.Resources$NotFoundException: Resource ID

android.content.res.Resources$NotFoundException: Resource ID #0xff412804問題原因 該異常表示 Android 系統嘗試通過資源 ID 查找資源&#xff0c;例如&#xff0c;顏色、圖片等&#xff0c;但未查找到對應資源 其中&#xff0c;0xff412804 是一個硬編碼的整型顏色值&…

03.自動特征提取(深度學習)核心邏輯:通過多層非線性變換,讓模型自動學習從原始數據到高層特征的映射。為什么多層非線性變換可以達到這樣的效果?

在深度學習中,多層非線性變換能夠實現自動特征提取的核心原因在于其對數據表征的分層學習能力和非線性映射的表達優勢。以下從理論基礎、數學機制、實際效果三個層面展開解析: 一、非線性變換的本質:突破線性模型的表達局限 線性模型的局限性 線性變換(如矩陣乘法)只能學…

42-Oracle 23 ai 安全新特性(Audit統一審計)

小伙伴們業務和安全運維中需要數據庫審計都是由哪些模塊來實現的&#xff0c;專門的第三方產品嗎&#xff1f;在醫療領域防統方等業務場景和數據庫的審計集合很是緊密。 在Oracle逐個版本的演進中&#xff0c;Oracle 23ai 的審計特性在安全領域的重大革新&#xff0c;延續傳統…

Python 爬蟲入門 Day 4 - 模擬登錄爬蟲與 Session 維持

Python 第二階段 - 爬蟲入門 &#x1f3af; 今日目標 學習什么是 Cookie / Session&#xff0c;為什么要維持登錄狀態掌握 requests.Session 用法模擬登錄一個帶登錄表單的網站獲取登錄后的頁面內容 &#x1f4d8; 學習內容詳解 &#x1f510; 什么是 Session&#xff1f; …

新零售系統商城開發全解析

一、新零售系統商城概述? (一)新零售的概念? 新零售依托互聯網與物聯網技術,以數據驅動為核心,打破線上線下的界限,構建起一體化的全新零售模式。它不再局限于傳統的銷售渠道,而是通過整合線上電商平臺、線下實體店鋪以及現代物流配送等多方面資源,實現商品、服務、…

c++基礎入門——c++初識

我看的是B站黑馬程序員的課《C教程》。準備用這個專欄記錄一下學習筆記。 這套c課程的課程安排如下&#xff1a; 階段內容目標案例第一階段C基礎語法入門對c有初步了解&#xff0c;能夠有基礎編程能力通訊錄管理系統第二階段c核心編程介紹c面向對象編程&#xff0c;為大型項目…

【css】設置了margin-top為負數,div被img覆蓋的解決方法

文章目錄 場景默認情況下&#xff0c;層疊順序是如何工作的&#xff1f;為什么 img 會覆蓋 div&#xff1f;解決方法 場景 <img src"image.jpg"> <div>Content</div>有代碼如上&#xff0c;img src是一個https網絡圖片鏈接。 若div的margin-top為…

4 Studying《ARM System Developer’s Guide》1-7

目錄 Preface Chapter1 ARM Embedded Systems 1.1 The RISC design philosophy 1.2 The ARM Design Philosophy 1.3 Embedded System Hardware 1.4 Embedded System Software 1.5 Summary Chapter2 ARM Processor Fundamentals 2.1 Registers 2.2 Current Program St…

Vue3 + Axios + Ant Design Vue 請求封裝詳解教程(含 Token 鑒權、加密、下載)

Vue3 Axios Ant Design Vue 請求封裝詳解教程&#xff08;含 Token 鑒權、加密、下載&#xff09; 一、完整源碼&#xff08;請先閱讀&#xff09; import { message, Modal } from ant-design-vue; import axios from axios; import { localRead } from //utils/local-util…

SQL注入安全研究

?據OWASP 2023報告顯示&#xff0c;SQL注入連續15年位居Web安全威脅榜首&#xff0c;在應用漏洞中占比34.1%?? ?NIST統計顯示&#xff1a;2022-2023年高危SQL注入漏洞同比增長27%&#xff0c;企業平均修復成本達$320,000? 一、漏洞本質與技術原理解析 1. SQL注入核心機理…

Ubuntu最新版本(Ubuntu22.04LTS)安裝nfs服務器

NFS&#xff08;Network File System&#xff09;是一種允許不同計算機之間共享文件的網絡文件系統。 在Ubuntu 22.04 LTS中&#xff0c;您可以使用以下步驟安裝并配置NFS服務器。 一、安裝NFS服務器 在Ubuntu 22.04 LTS中&#xff0c;您可以使用以下命令安裝NFS服務器&…

學習筆記丨數字信號處理(DSP)的應用——圖像處理篇

&#x1f4cc; DSP在圖像處理中的應用&#xff1a;核心技術解析 數字信號處理&#xff08;DSP&#xff09;是圖像處理的核心技術之一&#xff0c;廣泛應用于增強、壓縮、分析和識別等領域。以下是DSP在圖像處理中的關鍵應用及技術細節&#xff1a; 目錄 &#x1f50d; 圖像增…

Kafka Broker處理消費者請求源碼深度解析:從請求接收到數據返回

在Kafka生態體系中&#xff0c;消費者從Broker拉取消息是實現數據消費的關鍵環節。Broker如何高效處理消費者請求&#xff0c;精準定位并返回對應分區數據&#xff0c;直接決定了整個消息系統的性能與穩定性。接下來&#xff0c;我們將聚焦Kafka Broker端&#xff0c;深入剖析其…

Objective-C與Swift混合編程

Objective-C與Swift混合編程的基本概念 Objective-C與Swift混合編程是指在同一項目中同時使用兩種語言進行開發。這種混合編程方式在遷移舊項目或利用Swift新特性時非常有用。兩種語言可以相互調用&#xff0c;但需要遵循特定的規則和橋接機制。 設置混合編程環境 在Xcode項…

IDE深度集成+實時反饋:企業級軟件測試方案Parasoft如何重塑汽車巨頭的測試流程

在汽車行業數字化轉型的浪潮中&#xff0c;全球第四大汽車集團Stellantis曾面臨嚴峻的測試效率挑戰&#xff1a;開發與測試流程脫節、團隊對“測試左移”策略的抵觸、TDD&#xff08;測試驅動開發&#xff09;推進困難……這些痛點直接導致質量保障滯后&#xff0c;拖慢產品交付…

【Linux】Linux異步I/O -libaio

一、libaio 原理概述 1.1 libaio 介紹 libaio&#xff08;Linux Asynchronous I/O&#xff09;是 Linux 內核提供的異步 I/O 庫&#xff0c;其核心原理是&#xff1a; 異步提交&#xff1a;應用程序通過 io_submit 提交 I/O 請求后立即返回&#xff0c;不阻塞進程事件通知&a…

git submodule 和git repo介紹

這是一個 Git 子模塊&#xff08;submodule&#xff09;管理問題。當一個 Git 倉庫&#xff08;主倉庫&#xff09;中包含多個其他 Git 倉庫&#xff08;子倉庫&#xff09;時&#xff0c;最推薦的做法是使用 Git 子模塊 或 Git 子樹&#xff08;subtree&#xff09; 進行管理。…