環境篇:嘔心瀝血@CDH線上調優

環境篇:嘔心瀝血@CDH線上調優

環境篇:嘔心瀝血@CDH線上調優

  • 為什麼出這篇文章?

近期有很多公司開始引入大數據,由於各方資源有限,並不能合理分配服務器資源,和服務器選型,小恭弘=叶 恭弘這裏將工作中的總結出來,給新入行的小夥伴帶個方向,不敢說一定對,但是本人親自測試,發現集群使用率穩定提高了3分之1,最高可達到2分之1,有不對的地方歡迎留言指出。

注:可能有些服務沒有設計,使用到的小夥伴可以參照這種方式去規劃。

0 資源:集群服務安排

服務名稱 子服務 CM-64G ZK-Kafka(3台)-12G DataNode(3台)-64G NameNode1-64G NameNode2-64G Resourcemanager1-32G Resourcemanager2-32G hive-hbase-16G hive-hbase-16G
MySQL MySQL
CM Activity Monitor
Alert Publisher
Event Server
Host Monitor
Service Monitor




HDFS NameNode
DataNode
Failover Controller
JournalNode
X

X
X

X


X

X
X
X
Yarn NodeManager
Resourcemanager
JobHisoryServer

X
X
X

X

Zookeeper Zookeeper Server
Kafka Kafka Broker
Hive Hive Metastore Server
HiveServer2
Gateway(安裝對應應用服務器)
X


X

X
Hbase HMaster
HRegionServer
Thrift Server
X


X

X
Oozie Oozie Server
Hue Hue Server
Load Balancer
X

X
Spark History Server
Gateway(安裝對應應用服務器)

X
Flume Flume Agent (安裝對應應用服務器)
Sqoop Sqoop(安裝對應應用服務器)

1 優化:Cloudera Management

1.1 Cloudera Management Service

這些服務主要是提供監控功能,目前的調整主要集中在內存放,以便有足夠的資源 完成集群管理。

服務 選項 配置值
Activity Monitor Java Heap Size 2G
Alert Publisher Java Heap Size 2G
Event Server Java Heap Size 2G
Host Monitor Java Heap Size 4G
Service Monitor Java Heap Size 4G
Reports Manager Java Heap Size 2G
Navigator Metadata Server Java Heap Size 8G

2 優化:Zookeeper

服務 選項 配置值
Zookeeper Java Heap Size (堆棧大小) 4G
Zookeeper maxClientCnxns (最大客戶端連接數) 1024
Zookeeper dataDir (數據文件目錄+數據持久化路徑) /hadoop/zookeeper (建議獨立目錄)
Zookeeper dataLogDir (事務日誌目錄) /hadoop/zookeeper_log (建議獨立目錄)

3 優化:HDFS

3.1 磁盤測試

3.1.1 讀測試

hdparm 用於查看硬盤的相關信息或對硬盤進行測速、優化、修改硬盤相關參數設定

#安裝hdparm
yum install hdparm
#獲取硬盤符
fdisk -l
#讀測試(讀取上一步獲取硬盤符)
hdparm -t /dev/vda

三次測試結果:

Timing buffered disk reads: 500 MB in 0.84 seconds = 593.64 MB/sec

Timing buffered disk reads: 500 MB in 0.93 seconds = 538.80 MB/sec

Timing buffered disk reads: 500 MB in 0.74 seconds = 672.95 MB/sec

說明:接近1s秒讀取了500MB磁盤,讀速度約 500 MB/秒

3.1.2 寫測試

dd 這裏使用 time + dd 簡單測試寫速度,不要求很精確

查看內存緩存情況
free -m
 
清除緩存
sync; echo 3 > /proc/sys/vm/drop_caches
 
查block size
blockdev --getbsz /dev/vda
 
寫測試
echo 3 > /proc/sys/vm/drop_caches; time dd if=/dev/zero of=/testdd bs=4k count=100000

三次測試結果:

記錄了100000+0 的讀入
記錄了100000+0 的寫出

409600000 bytes (410 MB) copied, 0.574066 s, 714 MB/s –410MB複製,用時0.57秒,評估714M/s

409600000 bytes (410 MB) copied, 1.84421 s, 222 MB/s –410MB複製,用時1.84秒,評估222 M/s

409600000 bytes (410 MB) copied, 1.06969 s, 383 MB/s –410MB複製,用時1.06秒,評估383M/s

3.1.3 網絡帶寬

iperf3測量一個網絡最大帶寬

#安裝iperf3
yum -y install iperf3
 
#服務端
iperf3 -s
 
#客戶端
iperf3 -c 上調命令執行的服務機器IP

測試結果:

[ ID]–>線程id Interva–>傳輸時間 Transfer–>接收數據大小 Bandwidth–>帶寬每秒大小 Retr 角色
[ 4] 0.00-10.00 sec 17.0 GBytes 14.6 Gbits/sec 0 sender–>發送
[ 4] 0.00-10.00 sec 17.0 GBytes 14.6 Gbits/sec receiver–>接收

3.2 官方壓測

3.2.1 用戶準備

由於只能使用yarn配置了允許用戶,故這裏選擇hive用戶,如果su hive不能進入,則需要配置該步驟

usermod -s /bin/bash  hive
su hive

3.2.2 HDFS 寫性能測試

  • 測試內容:HDFS集群寫入10個128M文件(-D指定文件存儲目錄)
hadoop  jar /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/jars/hadoop-mapreduce-client-jobclient-3.0.0-cdh6.2.0-tests.jar  TestDFSIO  -D test.build.data=/test/benchmark -write -nrFiles 10 -fileSize 128

INFO fs.TestDFSIO: —– TestDFSIO —– : write
INFO fs.TestDFSIO: Date & time: Thu Jun 11 10:30:36 CST 2020
INFO fs.TestDFSIO: Number of files: 10 –十個文件
INFO fs.TestDFSIO: Total MBytes processed: 1280 –總大小1280M
INFO fs.TestDFSIO: Throughput mb/sec: 16.96 –吞吐量 每秒16.96M
INFO fs.TestDFSIO: Average IO rate mb/sec: 17.89 –平均IO情況17.89M
INFO fs.TestDFSIO: IO rate std deviation: 4.74 –IO速率標準偏差
INFO fs.TestDFSIO: Test exec time sec: 46.33 –總運行時間

3.2.3 HDFS 讀性能測試

  • 測試內容:HDFS集群讀取10個128M文件
hadoop jar /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/jars/hadoop-mapreduce-client-jobclient-3.0.0-cdh6.2.0-tests.jar TestDFSIO  -D test.build.data=/test/benchmark -read -nrFiles 10 -fileSize 128

INFO fs.TestDFSIO: —– TestDFSIO —– : read
INFO fs.TestDFSIO: Date & time: Thu Jun 11 10:41:19 CST 2020
INFO fs.TestDFSIO: Number of files: 10 –文件數
INFO fs.TestDFSIO: Total MBytes processed: 1280 –總大小
INFO fs.TestDFSIO: Throughput mb/sec: 321.53 –吞吐量 每秒321.53M
INFO fs.TestDFSIO: Average IO rate mb/sec: 385.43 –平均IO情況385.43M
INFO fs.TestDFSIO: IO rate std deviation: 107.67 –IO速率標準偏差
INFO fs.TestDFSIO: Test exec time sec: 20.81 –總運行時間

3.2.4 刪除測試數據

hadoop jar /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/jars/hadoop-mapreduce-client-jobclient-3.0.0-cdh6.2.0-tests.jar TestDFSIO  -D test.build.data=/test/benchmark -clean

3.3 參數調優

服務 選項 配置值
NameNode Java Heap Size (堆棧大小) 56G
NameNode dfs.namenode.handler.count (詳見3.3.2) 80
NameNode dfs.namenode.service.handler.count (詳見3.3.2) 80
NameNode fs.permissions.umask-mode (使用默認值022) 027(使用默認值022)
DataNode Java Heap Size (堆棧大小) 8G
DataNode dfs.datanode.failed.volumes.tolerated (詳見3.3.3) 1
DataNode dfs.datanode.balance.bandwidthPerSec (DataNode 平衡帶寬) 100M
DataNode dfs.datanode.handler.count (服務器線程數) 64
DataNode dfs.datanode.max.transfer.threads (最大傳輸線程數) 20480
JournalNode Java Heap Size (堆棧大小) 1G

3.3.1 數據塊優化

dfs.blocksize = 128M

  • 文件以塊為單位進行切分存儲,塊通常設置的比較大(最小6M,默認128M),根據網絡帶寬計算最佳值。
  • 塊越大,尋址越快,讀取效率越高,但同時由於MapReduce任務也是以塊為最小單位來處理,所以太大的塊不利於於對數據的并行處理。
  • 一個文件至少佔用一個塊(如果一個1KB文件,佔用一個塊,但是佔用空間還是1KB)
  • 我們在讀取HDFS上文件的時候,NameNode會去尋找block地址,尋址時間為傳輸時間的1%時,則為最佳狀態。
    • 目前磁盤的傳輸速度普遍為100MB/S
    • 如果尋址時間約為10ms,則傳輸時間=10ms/0.01=1000ms=1s
    • 如果傳輸時間為1S,傳輸速度為100MB/S,那麼一秒鐘我們就可以向HDFS傳送100MB文件,設置塊大小128M比較合適。
    • 如果帶寬為200MB/S,那麼可以將block塊大小設置為256M比較合適。

3.3.2 NameNode 的服務器線程的數量

  • dfs.namenode.handler.count=20*log2(Cluster Size),比如集群規模為16 ,8以2為底的對數是4,故此參數設置為80
  • dfs.namenode.service.handler.count=20*log2(Cluster Size),比如集群規模為16 ,8以2為底的對數是4,故此參數設置為80

NameNode有一個工作線程池,用來處理不同DataNode的併發心跳以及客戶端併發的元數據操作。該值需要設置為集群大小的自然對數乘以20,。

3.3.3 DataNode 停止提供服務前允許失敗的卷的數量

DN多少塊盤損壞后停止服務,默認為0,即一旦任何磁盤故障DN即關閉。 對盤較多的集群(例如DN有超過2塊盤),磁盤故障是常態,通常可以將該值設置為1或2,避免頻繁有DN下線。

4 優化:YARN + MapReduce

服務 選項 配置值 參數說明
ResourceManager Java Heap Size (堆棧大小) 4G
ResourceManager yarn.scheduler.minimum-allocation-mb (最小容器內存) 2G 給應用程序 Container 分配的最小內存
ResourceManager yarn.scheduler.increment-allocation-mb (容器內存增量) 512M 如果使用 Fair Scheduler,容器內存允許增量
ResourceManager yarn.scheduler.maximum-allocation-mb (最大容器內存) 32G 給應用程序 Container 分配的最大內存
ResourceManager yarn.scheduler.minimum-allocation-vcores (最小容器虛擬 CPU 內核數量) 1 每個 Container 申請的最小 CPU 核數
ResourceManager yarn.scheduler.increment-allocation-vcores (容器虛擬 CPU 內核增量) 1 如果使用 Fair Scheduler,虛擬 CPU 內核允許增量
ResourceManager yarn.scheduler.maximum-allocation-vcores (最大容器虛擬 CPU 內核數量) 16 每個 Container 申請的最大 CPU 核數
ResourceManager yarn.resourcemanager.recovery.enabled true 啟用后,ResourceManager 中止時在群集上運行的任何應用程序將在 ResourceManager 下次啟動時恢復,備註:如果啟用 RM-HA,則始終啟用該配置。
NodeManager Java Heap Size (堆棧大小) 4G
NodeManager yarn.nodemanager.resource.memory-mb 40G 可分配給容器的物理內存數量,參照資源池內存90%左右
NodeManager yarn.nodemanager.resource.cpu-vcores 32 可以為容器分配的虛擬 CPU 內核的數量,參照資源池內存90%左右
ApplicationMaster yarn.app.mapreduce.am.command-opts 右紅 傳遞到 MapReduce ApplicationMaster 的 Java 命令行參數 “-Djava.net.preferIPv4Stack=true
ApplicationMaster yarn.app.mapreduce.am.resource.mb (ApplicationMaster 內存) 4G
JobHistory Java Heap Size (堆棧大小) 2G
MapReduce mapreduce.map.memory.mb (Map 任務內存) 4G 一個MapTask可使用的資源上限。如果MapTask實際使用的資源量超過該值,則會被強制殺死。
MapReduce mapreduce.reduce.memory.mb (Reduce 任務內存) 8G 一個 ReduceTask 可使用的資源上限。如果 ReduceTask 實際使用的資源量超過該值,則會被強制殺死
MapReduce mapreduce.map.cpu.vcores 2 每個 MapTask 可使用的最多 cpu core 數目
MapReduce mapreduce.reduce.cpu.vcores 4 每個 ReduceTask 可使用的最多 cpu core 數目
MapReduce mapreduce.reduce.shuffle.parallelcopies 20 每個 Reduce 去 Map 中取數據的并行數。
MapReduce mapreduce.task.io.sort.mb(Shuffle 的環形緩衝區大小) 512M 當排序文件時要使用的內存緩衝總量。注意:此內存由 JVM 堆棧大小產生(也就是:總用戶 JVM 堆棧 – 這些內存 = 總用戶可用堆棧空間)
MapReduce mapreduce.map.sort.spill.percent 80% 環形緩衝區溢出的閾值
MapReduce mapreduce.task.timeout 10分鐘 Task 超時時間,經常需要設置的一個參數,該參數表 達的意思為:如果一個 Task 在一定時間內沒有任何進 入,即不會讀取新的數據,也沒有輸出數據,則認為 該 Task 處於 Block 狀態,可能是卡住了,也許永遠會 卡住,為了防止因為用戶程序永遠 Block 住不退出, 則強制設置了一個該超時時間。如果你的程序對每條輸入數據的處理時間過長(比如會訪問數據庫,通過網絡拉取數據等),建議將該參數調大,該參數過小常出現的錯誤提示是 :AttemptID:attempt_12267239451721_123456_m_00 0335_0 Timed out after 600 secsContainer killed by the ApplicationMaster。

5 優化:Impala

服務 選項 配置值 參數說明
Impala Daemon mem_limit (內存限制) 50G 由守護程序本身強制執行的 Impala Daemon 的內存限制。
如果達到該限制,Impalad Daemon 上運行的查詢可能會被停止
Impala Daemon Impala Daemon JVM Heap 512M 守護進程堆棧大小
Impala Daemon scratch_dirs 節點上多塊獨立磁盤(目錄) Impala Daemon 將溢出信息等數據寫入磁盤以釋放內存所在的目錄。這可能是大量數據
Impala Catalog Server Java Heap Size 8G 堆棧大小

6 優化:Kafka

6.1 官方壓測

6.1.1 Kafka Producer 壓力測試

  • record-size 是一條信息有多大,單位是字節。
  • num-records 是總共發送多少條信息。
  • throughput 是每秒多少條信息,設成-1,表示不限流,可測出生產者最大吞吐量。
bash /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/kafka//bin/kafka-producer-perf-test.sh --topic test --record-size 100 --num-records 100000 --throughput -1 --producer-props bootstrap.servers=cdh01.cm:9092,cdh02.cm:9092,cdh03.cm:9092

100000 records sent, 225733.634312 records/sec (21.53 MB/sec),

8.20 ms avg latency, 66.00 ms max latency,

3 ms 50th, 28 ms 95th, 30 ms 99th, 30 ms 99.9th.

參數解析:一共寫入 10w 條消息,吞吐量為 21.53 MB/sec,每次寫入的平均延遲

為 8.20 毫秒,最大的延遲為 66.00 毫秒。

6.1.2 Kafka Consumer 壓力測試

  • zookeeper 指定 zookeeper 的鏈接信息
  • topic 指定 topic 的名稱
  • fetch-size 指定每次 fetch 的數據的大小
  • messages 總共要消費的消息個數
bash /opt/cloudera/parcels/CDH-6.2.0-1.cdh6.2.0.p0.967373/lib/kafka//bin/kafka-consumer-perf-test.sh --broker-list cdh01.cm:9092,cdh02.cm:9092,cdh03.cm:9092 --topic test --fetch-size 10000 --messages 10000000 --threads 1

start.time, end.time, data.consumed.in.MB, MB.sec, data.consumed.in.nMsg, nMsg.sec, rebalance.time.ms, fetch.time.ms, fetch.MB.sec, fetch.nMsg.sec

2020-06-11 17:53:48:179, 2020-06-11 17:54:04:525, 57.2205, 3.5006, 600000, 36706.2278, 3051, 13295, 4.3039, 45129.7480

start.time:2020-06-11 17:53:48:179 開始時間

end.time:2020-06-11 17:54:04:525 結束時間(用時16秒)

data.consumed.in.MB:57.2205 消費57M數據

MB.sec:3.5006 3.5M/S

data.consumed.in.nMsg:600000 消費60萬消息

nMsg.sec:36706.2278 36706條消息/S

rebalance.time.ms:3051 平衡時間3S

fetch.time.ms:13295 抓取時間13S

fetch.MB.sec:4.3039 一秒抓取4.3M

fetch.nMsg.sec:45129.7480 一秒抓取45129條消息

開始測試時間,測試結束數據,共消費數據57.2205MB,吞吐量 3.5M/S,共消費600000條,平均每秒消費36706.2278條。

6.1.3 Kafka 機器數量計算

Kafka 機器數量(經驗公式)= 2 X(峰值生產速度 X 副本數 /100)+ 1

先拿到峰值生產速度,再根據設定的副本數,就能預估出需要部署 Kafka 的數量。

比如我們的峰值生產速度是 50M/s。副本數為 2。

Kafka 機器數量 = 2 X( 50 X 2 / 100 )+ 1 = 3 台

6.2 參數調優

服務 選項 配置值 參數說明
Kafka Broker Java Heap Size of Broker 2G Broker堆棧大小
Kafka Broker Data Directories 多塊獨立磁盤
Kafka 服務 Maximum Message Size 10M 服務器可以接收的消息的最大大小。此屬性必須與使用者使用的最大提取大小同步。否則,不守規矩的生產者可能會發布太大而無法消費的消息
Kafka 服務 Replica Maximum Fetch Size 20M 副本發送給leader的獲取請求中每個分區要獲取的最大字節數。此值應大於message.max.bytes。
Kafka 服務 Number of Replica Fetchers 6 用於複製來自領導者的消息的線程數。增大此值將增加跟隨者代理中I / O并行度。

7 優化:HBase

服務 選項 配置值 參數說明
HBase Java Heap Size 18G 客戶端 Java 堆大小(字節)主要作用來緩存Table數據,但是flush時會GC,不要太大,根據集群資源,一般分配整個Hbase集群內存的70%,16->48G就可以了
HBase hbase.client.write.buffer 512M 寫入緩衝區大小,調高該值,可以減少RPC調用次數,單數會消耗更多內存,較大緩衝區需要客戶端和服務器中有較大內存,因為服務器將實例化已通過的寫入緩衝區並進行處理,這會降低遠程過程調用 (RPC) 的數量。
HBase Master Java Heap Size 8G HBase Master 的 Java 堆棧大小
HBase Master hbase.master.handler.count 300 HBase Master 中啟動的 RPC 服務器實例數量。
HBase RegionServer Java Heap Size 31G HBase RegionServer 的 Java 堆棧大小
HBase RegionServer hbase.regionserver.handler.count 100 RegionServer 中啟動的 RPC 服務器實例數量,根據集群情況,可以適當增加該值,主要決定是客戶端的請求數
HBase RegionServer hbase.regionserver.metahandler.count 60 用於處理 RegionServer 中的優先級請求的處理程序的數量
HBase RegionServer zookeeper.session.timeout 180000ms ZooKeeper 會話延遲(以毫秒為單位)。HBase 將此作為建議的最長會話時間傳遞給 ZooKeeper 仲裁
HBase RegionServer hbase.hregion.memstore.flush.size 1G 如 memstore 大小超過此值,Memstore 將刷新到磁盤。通過運行由 hbase.server.thread.wakefrequency 指定的頻率的線程檢查此值。
HBase RegionServer hbase.hregion.majorcompaction 0 合併周期,在合格節點下,Region下所有的HFile會進行合併,非常消耗資源,在空閑時手動觸發
HBase RegionServer hbase.hregion.majorcompaction.jitter 0 抖動比率,根據上面的合併周期,有一個抖動比率,也不靠譜,還是手動好
HBase RegionServer hbase.hstore.compactionThreshold 6 如在任意一個 HStore 中有超過此數量的 HStoreFiles,則將運行壓縮以將所有 HStoreFiles 文件作為一個 HStoreFile 重新寫入。(每次 memstore 刷新寫入一個 HStoreFile)您可通過指定更大數量延長壓縮,但壓縮將運行更長時間。在壓縮期間,更新無法刷新到磁盤。長時間壓縮需要足夠的內存,以在壓縮的持續時間內記錄所有更新。如太大,壓縮期間客戶端會超時。
HBase RegionServer hbase.client.scanner.caching 1000 內存未提供數據的情況下掃描儀下次調用時所提取的行數。較高緩存值需啟用較快速度的掃描儀,但這需要更多的內存且當緩存為空時某些下一次調用會運行較長時間
HBase RegionServer hbase.hregion.max.filesize 50G HStoreFile 最大大小。如果列組的任意一個 HStoreFile 超過此值,則託管 HRegion 將分割成兩個
HBase Master hbase.master.logcleaner.plugins 日誌清除器插件 org.apache.hadoop.hbase.master.cleaner.TimeToLiveLogCleaner
HBase hbase.replication false 禁用複製
HBase hbase.master.logcleaner.ttl 10min 保留 HLogs 的最長時間,加上如上兩條解決oldWALs增長問題

8 優化:Hive

服務 選項 配置值 參數說明
HiveServer2 Java Heap Size 4G
Hive MetaStore Java Heap Size 8G
Hive Gateway Java Heap Size 2G
Hive hive.execution.engine Spark 執行引擎切換
Hive hive.fetch.task.conversion more Fetch抓取修改為more,可以使全局查找,字段查找,limit查找等都不走計算引擎,而是直接讀取表對應儲存目錄下的文件,大大普通查詢速度
Hive hive.exec.mode.local.auto(hive-site.xml 服務高級配置,客戶端高級配置) true 開啟本地模式,在單台機器上處理所有的任務,對於小的數據集,執行時間可以明顯被縮短
Hive hive.exec.mode.local.auto.inputbytes.max(hive-site.xml 服務高級配置,客戶端高級配置) 50000000 文件不超過50M
Hive hive.exec.mode.local.auto.input.files.max(hive-site.xml 服務高級配置,客戶端高級配置) 10 個數不超過10個
Hive hive.auto.convert.join 開啟 在join問題上,讓小表放在左邊 去左鏈接(left join)大表,這樣可以有效的減少內存溢出錯誤發生的幾率
Hive hive.mapjoin.smalltable.filesize(hive-site.xml 服務高級配置,客戶端高級配置) 50000000 50M以下認為是小表
Hive hive.map.aggr 開啟 默認情況下map階段同一個key發送給一個reduce,當一個key數據過大時就發生數據傾斜。
Hive hive.groupby.mapaggr.checkinterval(hive-site.xml 服務高級配置,客戶端高級配置) 200000 在map端進行聚合操作的條目數目
Hive hive.groupby.skewindata(hive-site.xml 服務高級配置,客戶端高級配置) true 有數據傾斜時進行負載均衡,生成的查詢計劃會有兩個MR Job,第一個MR Job會將key加隨機數均勻的分佈到Reduce中,做部分聚合操作(預處理),第二個MR Job在根據預處理結果還原原始key,按照Group By Key分佈到Reduce中進行聚合運算,完成最終操作
Hive hive.exec.parallel(hive-site.xml 服務高級配置,客戶端高級配置) true 開啟并行計算
Hive hive.exec.parallel.thread.number(hive-site.xml 服務高級配置,客戶端高級配置) 16G 同一個sql允許的最大并行度,針對集群資源適當增加

9 優化:Oozie、Hue

服務 選項 配置值 參數說明
Oozie Java Heap Size 1G 堆棧大小
Hue Java Heap Size 4G 堆棧大小

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化