主頁 > 軟體設計 > Docker部署MongoDB分片+副本集集群(實戰)

Docker部署MongoDB分片+副本集集群(實戰)

2020-12-11 12:07:23 軟體設計

引言

本次實踐部署mongodb集群,
主要借鑒于該博客(https://blog.csdn.net/weixin_42104521/article/details/103731266),

一、原理簡析

Mongodb一共有三種集群搭建的方式:
Replica Set(副本集)、
Sharding(切片)
Master-Slaver(主從)

mongoDB目前已不推薦使用主從模式,取而代之的是副本集模式,副本集其實一種互為主從的關系,可理解為主主,
副本集:指將資料復制,多份保存,不同服務器保存同一份資料,在出現故障時自動切換,對應的是資料冗余、備份、鏡像、讀寫分離、高可用性等關鍵詞;
分片:則指為處理大量資料,將資料分開存盤,不同服務器保存不同的資料,它們的資料總和即為整個資料集,追求的是高性能,

本實驗模擬的MongoDB集群分以下幾個層次或角色

mongos層:請求的入口,是router的角色,相當于監聽,負責將請求分發到對應的存盤資料的shard上,多副本冗余

config server層:記錄了mongos中使用到的元資料,自動向mongos同步最新的集群配置,多副本冗余

shard主節點層:將資料分片,資料庫拆分,并將其分散在不同的機器上,原理是將整個資料集合切塊
塊分散到各個shard中,每個shard只負責總資料的一部分,通過一個均衡器來對各個shard均衡,多副本冗余

shard副本層:是shard的備份,多副本冗余

shard仲裁層:用于仲裁,不存盤資料,使用最小的資源,需要基數個仲裁角色,且不能放在同一設備上

主機角色埠規劃

本次實踐在自建虛擬機進行,有個別需求的同學,可以購買華為云ECS進行部署(當前優惠力度大哦) 華為云-2核4G云主機低至487元/年

服務器192.168.1.32192.168.1.33192.168.1.35
服務埠mongos:27017mongos:27017mongos:27017
服務埠config server:9001config server:9001config server:9001
服務埠shard1 主節點:9005shard1 副節點:9005shard1 仲裁節點:9005
服務埠shard2 仲裁節點:9006shard2 主節點:9006shard2 副節點:9006
服務埠shard3 副節點:9007shard3 仲裁節點:9007shard3 主節點:9007

可以看到每臺主機上有1個mongos、1個config server、3個分片,三臺主機的相同分片之間構成了主、副和仲裁三個角色,

總結

應用請求mongos來操作mongodb的增刪改查
配置服務器存盤資料庫元資訊,并且和mongos做同步
資料最終存入在shard(分片)上
為了防止資料丟失同步在副本集中存盤了一份
仲裁在資料存盤到分片的時候決定存盤到哪個節點

二、部署配置服務器

創建掛載目錄、組態檔

mkdir /data/mongod/configdata/configsvr -p   //三臺主機都執行
mkdir /data/mongod/conf/{configsvr,keyfile} -p   // keyfile在開啟用戶認證時需要

組態檔

# vim /data/mongod/conf/configsvr/mongod.conf
net:
  bindIpAll: true
replication:
  replSetName: rs_configsvr # 副本集名稱,相同副本須使用同一個副本集名稱
  
sharding: 
   clusterRole: configsvr   # 定義為mongo配置服務器

啟動configsvr

## 三臺主機均執行此操作
docker run -d -p 9001:27019 --name configsvr \
  --entrypoint "mongod" \
  -v /data/mongod/configdata/configsvr:/data/configdb \
  -v /data/mongod/conf/keyfile:/data/keyfile \
  -v /data/mongod/conf/configsvr:/data/conf \
  mongo:4.0.21 -f /data/conf/mongod.conf

備注:如果docker ps 沒有剛才創建的容器名稱,可以使用docker logs <容器id>查看docker 日志

初始化配置服務復制集

# 進入容器中,創建的三個配置服務中隨便一個
docker exec -it configsvr bash
 
# 登錄mongo
mongo --host 192.168.1.32 --port 9001
 
# 初始化
use admin
rs.initiate({
    _id: "rs_configsvr",
    configsvr: true,
    members: [
            { _id : 0, host : "192.168.1.32:9001" },
            { _id : 1, host : "192.168.1.33:9001" },
            { _id : 2, host : "192.168.1.35:9001" }
        ]
    }
)

rs.status()   //查看狀態

三、創建分片副本集

創建分片副本集組態檔、掛載檔案

## 創建組態檔、資料目錄
mkdir /data/mongod/shard1/{log,db} -p
mkdir /data/mongod/shard2/{log,db} -p
mkdir /data/mongod/shard3/{log,db} -p
chmod -R 777 /data/mongod/shard1
chmod -R 777 /data/mongod/shard2
chmod -R 777 /data/mongod/shard3
mkdir -p /data/mongod/conf/{shard1,shard2,shard3}  

## 三臺主機均創建三個組態檔,shard分片名不同即可
#所有用到的檔案夾和檔案必須在創建的時候給權限,包括重新創建,也需要重新給權限,并且檔案夾要提前創建好
# vim /data/mongod/conf/{shard1,shard2,shard3}/mongod.conf   //添加如下配置資訊,分片服務名稱不一致即可
storage:
  dbPath: /home/mongod/db #分片資料庫路徑
  journal:
    enabled: true
  directoryPerDB: true

systemLog:
  destination: file
  logAppend: true
  path: /home/mongod/log/mongod.log # 分片日志

net:
  bindIpAll: true

setParameter:
  enableLocalhostAuthBypass: false

replication:
  replSetName: rs_shardsvr1  # 分片服務名稱
 
sharding: 
   clusterRole: shardsvr     # 配置為分片服務

注意事項:
1、需要提前建立需要的檔案夾,并賦予權限(chmod -R 750 檔案夾),
2、三個分片服務器建立在三個位置,(docker代碼 -v 引數即為實際建立需要的檔案夾),需要注意對應創建分片組態檔,檔案內容一致,
3、注意組態檔中的資料庫檔案報錯路徑以及日志檔案路徑,按需更改,
4、分片服務埠需要映射 27018

shard1 分片

1、mongod分片部署

## 三臺主機(192.168.1.32、33、35)執行如下啟動命令
docker run --name shardsvr1 -d -p 9005:27018 \
  --entrypoint "mongod" \
  -v /data/mongod/shard1:/home/mongod \
  -v /data/mongod/conf/keyfile/:/data/keyfile/ \
  -v /data/mongod/conf/shard1/:/data/conf/ \
  mongo:4.0.21 -f /data/conf/mongod.conf

2、初始化分片服務器

# 登錄進容器中,任意一臺
docker exec -it shardsvr1 bash
 
# 登錄分片服務器
# 192.168.1.32 主分片節點
# 192.168.1.33 副分片節點
# 192.168.1.35 仲裁分片節點,arbiterOnly:true 表示為仲裁節點

mongo --host 192.168.1.32 --port 9005
use admin
rs.initiate(
    {
        _id : "rs_shardsvr1",
        members: [
            { _id : 0, host : "192.168.1.32:9005",priority:5 },
            { _id : 1, host : "192.168.1.33:9005",priority:3 },
            { _id : 2, host : "192.168.1.35:9005",arbiterOnly:true }
        ]
    }
)
 
#rs.status() 查看狀態

shard2 分片

1、mongod分片部署

## 三臺主機(192.168.1.32、33、35)執行如下啟動命令
docker run --name shardsvr2 -d -p 9006:27018 \
  --entrypoint "mongod" \
  -v /data/mongod/shard2:/home/mongod \
  -v /data/mongod/conf/keyfile/:/data/keyfile/ \
  -v /data/mongod/conf/shard2/:/data/conf/ \
  mongo:4.0.21 -f /data/conf/mongod.conf

2、初始化分片服務器

# 登錄進容器中,盡量在主節點執行,否則會報錯
docker exec -it shardsvr2 bash

#登錄分片服務器
# 192.168.1.32 仲裁分片節點
# 192.168.1.33 主分片節點
# 192.168.1.35 副分片節點

mongo --host 192.168.1.33 --port 9006
use admin
rs.initiate(
    {
        _id : "rs_shardsvr2",
        members: [
            { _id : 0, host : "192.168.1.32:9006",arbiterOnly:true },
            { _id : 1, host : "192.168.1.33:9006",priority:5 },
            { _id : 2, host : "192.168.1.35:9006",priority:3 }
        ]
    }
)
 
#rs.status() 查看狀態

shard3 分片

1、mongod分片部署

## 三臺主機(192.168.1.32、33、35)執行如下啟動命令
docker run --name shardsvr3 -d -p 9007:27018 \
  --entrypoint "mongod" \
  -v /data/mongod/shard3:/home/mongod \
  -v /data/mongod/conf/keyfile/:/data/keyfile/ \
  -v /data/mongod/conf/shard3/:/data/conf/ \
  mongo:4.0.21 -f /data/conf/mongod.conf

2、初始化分片服務器

# 登錄進容器中,任意節點
docker exec -it shardsvr3 bash

# 登錄分片服務器
# 192.168.1.32 副分片節點
# 192.168.1.33 仲裁分片節點
# 192.168.1.35 主分片節點

mongo --host 192.168.1.35 --port 9007    // 盡量在主節點執行,否則可能會出現報錯
use admin
rs.initiate(
    {
        _id : "rs_shardsvr3",
        members: [
            { _id : 0, host : "192.168.1.32:9007",priority:3 },
            { _id : 1, host : "192.168.1.33:9007",arbiterOnly:true },
            { _id : 2, host : "192.168.1.35:9007",priority:5 }
        ]
    }
)
 
#rs.status() 查看狀態

四,創建mongos,連接mongos到分片集群

創建mongos組態檔

mkdir /data/mongod/conf/mongos -p   //三臺主機都執行

vim /data/mongod/conf/mongos/mongod.conf
## 組態檔
net:
  bindIpAll: true

sharding: 
   configDB: rs_configsvr/192.168.1.32:9001,192.168.1.33:9001,192.168.1.35:9001   # 定義為mongos配置服務器

mongos 部署

1、分片路由部署,三臺均執行相同操作

docker run --name mongos -d \
  -p 27017:27017 \
  --entrypoint "mongos" \
  -v /data/mongod/conf/keyfile/:/data/keyfile/ \
  -v /data/mongod/conf/mongos/:/data/conf/ \
  mongo:4.0.21 -f /data/conf/mongod.conf

備注:
rs_configsvr/192.168.1.32:9001,192.168.1.33:9001,192.168.1.35:9001: 配置服務名稱/配置服務埠(多個用逗號分隔)

2、初始化mongos

# 進入mongos 容器中
docker exec -it mongos bash
 
# 連接mongos
mongo --host 127.0.0.1 --port 27017
use admin
# 添加分片服務器
sh.addShard("rs_shardsvr1/192.168.1.32:9005,192.168.1.33:9005,192.168.1.35:9005")
sh.addShard("rs_shardsvr2/192.168.1.32:9006,192.168.1.33:9006,192.168.1.35:9006")
sh.addShard("rs_shardsvr3/192.168.1.32:9007,192.168.1.33:9007,192.168.1.35:9007")

sh.status() //查看狀態

開啟分片功能

## db和collection開啟分片功能
雖然資料庫采用分片集群的方式部署,但如果db和collection不啟用分片的話(默認是不啟用的),資料不會分片存盤,此時如果向集群中匯入一個db,會將整個db隨機存盤到任意一個分片中,而不是拆分存盤到多個分片,
db啟用分片:
sh.enableSharding("庫名")
sh.enableSharding("<database>")
將上述命令中的“”換成實際的db名,

collection啟用分片:
sh.shardCollection("庫名.集合名",{"key":1})
sh.shardCollection("<database>.<collection>", { <shard key> : "hashed" } )
上述命令中的“< database >.< collection >”為實際的db名和collection名;“{ < shard key > : “hashed” }”為片鍵的集合,

五,測驗使用

測驗使用

# 任意一節點操作
# 進入mongos 容器中
docker exec -it mongos bash
# 連接mongos
mongo --host 127.0.0.1 --port 27017
use admin

#  testdb1開啟分片功能
db.runCommand( { enablesharding  : "testdb1"});
db.runCommand( { shardcollection : "testdb1.tab1",key : {id: 1} } )

# 添加資料 
use testdb1;
for(var i=1;i<=20000;i++) db.tab1.save({id:i,"test1":"testval1"});

db.tab1.stats();
exit

# 使用testdb1庫
# 回圈插入資料到testdb1庫的tab1集合中的鍵id中
# 該庫對應的該集合對應的該鍵被設定成了分片
# 查看分片情況

分別在三個主機上操作配置庫、插入測驗資料、查看測驗資料
驗證了副本同步,最后的顯示結果看到 “sharded” : true 表示分片也是成功的

六、開啟登錄認證

設定資料庫賬號

在任意mongos節點操作
# 進入mongos 容器中
docker exec -it mongos bash
 
# 連接mongos
mongo --host 127.0.0.1 --port 27017
mongos> use admin
switched to db admin    
mongos> show collections


添加兩個管理員賬號,一個系統管理員:system 一個資料庫管理員:administrator
#先添加系統管理員賬號,用來管理用戶
mongos> db.createUser({user:"system",pwd:"123456",roles:[{role:"root",db:"admin"}]})

# 添加資料庫管理員,用來管理所有資料庫
mongos> db.createUser({user:'administrator', pwd:'123456', roles:[{ role: 
mongos> db.auth('administrator','123456')    //添加管理員用戶認證,認證之后才能管理所有資料庫   

# 退出,用剛才創建的賬號進行登錄
 mongo 192.168.1.33:27017 -u system -p 123456 --authenticationDatabase admin
 mongo 192.168.1.35:27017 -u administrator -p 123456  --authenticationDatabase admin

開啟登錄驗證

mkdir /data/mongod/conf/keyfile -p     //三臺主機都執行

# 在192.168.1.32節點服務器上操作
cd /data/mongod/conf/keyfile           //切換到指定目錄
openssl rand -base64 756 > key.file    //創建一個 keyfile(使用 openssl 生成 756 位 base64 加密的字串)   
chmod 600 ./key.file 

# 復制檔案到其他主機
scp ./key.file root@192.168.1.33:/data/mongod/conf/keyfile
scp ./key.file root@192.168.1.33:/data/mongod/conf/keyfile

設定組態檔

使用訪問控制強制重新啟動復制集的每個成員
# 依次在每臺機器上的mongod(注意是所有的mongod不是mongos)的組態檔中加入下面一段配置,如我在192.168.1.32上的config server,shard1,shard2,shard3都加入下面的組態檔
security:
  keyFile: /data/keyfile/key.file
  authorization: enabled
  
# 依次在每臺機器上的mongos組態檔中加入下面一段配置
security:
  keyFile: /data/keyfile/key.file

重啟集群

# 在三臺主機執行如下命令
docker ps  |grep mongo |awk -F " " '{print $1}' |xargs -r docker restart

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/233117.html

標籤:其他

上一篇:云計算的基本概念和常見測驗點

下一篇:了解OpenStak基礎架構

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more