敏捷之歌

我抽數故我存在 | DBus

人人玩轉流處理 | Wormhole

就當吾是資料庫 | Moonbox

顏值最後十公里 | Davinci

導讀:實時資料平臺(RTDP,Real-time Data Platform)是一個重要且常見的大資料基礎設施平臺。在上篇(設計篇)中,我們從現代數倉架構角度和典型資料處理角度介紹了RTDP,並探討了RTDP的整體設計架構。本文作為下篇(技術篇),則是從技術角度入手,介紹RTDP的技術選型和相關元件,探討適用不同應用場景的相關模式。RTDP的敏捷之路就此展開~

拓展閱讀:如何設計實時資料平臺(設計篇)

一、技術選型介紹

在設計篇中,我們給出了RTDP的一個整體架構設計(圖1)。在技術篇裡,我們則會推薦整體技術元件選型;對每個技術元件做出簡單介紹,尤其對我們抽象並實現的四個技術平臺(統一資料採集平臺、統一流式處理平臺、統一計算服務平臺、統一資料視覺化平臺)著重介紹設計思路;對Pipeline端到端切面話題進行探討,包括功能整合、資料管理、資料安全等。

圖1 RTDP架構

1.1 整體技術選型

圖2 整體技術選型

首先,我們簡要解讀一下圖2:

  • 資料來源、客戶端,列舉了大多數資料應用專案的常用資料來源型別。
  • 資料匯流排平臺DBus,作為統一資料採集平臺,負責對接各種資料來源。DBus將資料以增量或全量方式抽取出來,並進行一些常規資料處理,最後將處理後的訊息釋出在Kafka上。
  • 分散式訊息系統Kafka,以分散式、高可用、高吞吐、可釋出-訂閱等能力,連線訊息的生產者和消費者。
  • 流式處理平臺Wormhole,作為統一流式處理平臺,負責流上處理和對接各種資料目標儲存。Wormhole從Kafka消費訊息,支援流上配置SQL方式實現流上資料處理邏輯,並支援配置化方式將資料以最終一致性(冪等)效果落入不同資料目標儲存(Sink)中。
  • 在資料計算儲存層,RTDP架構選擇開放技術元件選型,使用者可以根據實際資料特性、計算模式、訪問模式、資料量等資訊選擇合適的儲存,解決具體資料專案問題。RTDP還支援同時選擇多個不同資料儲存,從而更靈活的支援不同專案需求。
  • 計算服務平臺Moonbox,作為統一計算服務平臺,對異構資料儲存端負責整合、計算下推優化、異構資料儲存混算等(資料虛擬化技術),對資料展示和互動端負責收口統一元資料查詢、統一資料計算和下發、統一資料查詢語言(SQL)、統一資料服務介面等。
  • 可視應用平臺Davinci,作為統一資料視覺化平臺,以配置化方式支援各種資料視覺化和互動需求,並可以整合其他資料應用以提供資料視覺化部分需求解決方案,另外還支援不同資料從業人員在平臺上協作完成各項日常資料應用。其他資料終端消費系統如資料開發平臺Zeppelin、資料演算法平臺Jupyter等在本文不做介紹。
  • 切面話題如資料管理、資料安全、開發運維、驅動引擎,可以通過對接DBus、Wormhole、Moonbox、Davinci的服務介面進行整合和二次開發,以支援端到端管控和治理需求。

下面我們會進一步細化上圖涉及到的技術元件和切面話題,介紹技術元件的功能特性,著重講解我們自研技術元件的設計思想,並對切面話題展開討論。

1.2 技術元件介紹

1.2.1 資料匯流排平臺DBus

圖3 RTDP架構之DBus

1.2.1.1 DBus設計思想

1)從外部角度看待設計思想

  • 負責對接不同的資料來源,實時抽取出增量資料,對於資料庫會採用操作日誌抽取方式,對於日誌型別支援與多種Agent對接。
  • 將所有訊息以統一的UMS訊息格式釋出在Kafka上,UMS是一種標準化的自帶元資料資訊的JSON格式,通過統一UMS實現邏輯訊息與物理Kafka Topic解耦,使得同一Topic可以流轉多個UMS訊息表。
  • 支援資料庫的全量資料拉取,並且和增量資料統一融合成UMS訊息,對下游消費透明無感知。

2)從內部角度看待設計思想

  • 基於Storm計算引擎進行資料格式化,確保訊息端到端延遲最低。
  • 對不同資料來源資料進行標準化格式化,生成UMS資訊,其中包括:

✔ 生成每條訊息的唯一單調遞增id,對應系統欄位ums_id_

✔ 確認每條訊息的事件時間戳(event timestamp),對應系統欄位ums_ts_

✔ 確認每條訊息的操作模式(增刪改,或insert only),對應系統欄位ums_op_

  • 對資料庫表結構變更實時感知並採用版本號進行管理,確保下游消費時明確上游元資料變化。
  • 在投放Kafka時確保訊息強有序(非絕對有序)和at least once語義。
  • 通過心跳錶機制確保訊息端到端探活感知。
1.2.1.2 DBus功能特性
  • 支援配置化全量資料拉取
  • 支援配置化增量資料拉取
  • 支援配置化線上格式化日誌
  • 支援視覺化監控預警
  • 支援配置化多租戶安全管控
  • 支援分表資料彙集成單邏輯表
1.2.1.3 DBus技術架構

圖4 DBus資料流轉架構圖

更多DBus技術細節和使用者介面,可以參看:

GitHub: https://github.com/BriData

1.2.2 分散式訊息系統Kafka

Kafka已經成為事實標準的大資料流式處理分散式訊息系統,當然Kafka在不斷的擴充套件和完善,現在也具備了一定的儲存能力和流式處理能力。關於Kafka本身的功能和技術已經有很多文章資訊可以查閱,本文不再詳述Kafka的自身能力。

這裡我們具體探討Kafka上訊息元資料管理(Metadata Management)和模式演變(Schema Evolution)的話題。

圖5 

圖片來源:http://cloudurable.com/images/kafka-ecosystem-rest-proxy-schema-registry.png

圖5顯示,在Kafka背後的Confluent公司解決方案中,引入了一個元資料管理元件:Schema Registry。這個元件主要負責管理在Kafka上流轉訊息的 元資料資訊和Topic資訊,並提供一系列元資料管理服務。之所以要引入這樣一個元件,是為了Kafka的消費方能夠了解不同Topic上流轉的是哪些資料,以及資料的元資料資訊,並進行有效的解析消費。

任何資料流轉鏈路,不管是在什麼系統上流轉,都會存在這段資料鏈路的元資料管理問題,Kafka也不例外。Schema Registry是一種中心化的Kafka資料鏈路元資料管理解決方案,並且基於Schema Registry,Confluent提供了相應的Kafka資料安全機制和模式演變機制。

更多關於Schema Registry的介紹,可以參看:

Kafka Tutorial:Kafka, Avro Serialization and the Schema Registry

http://cloudurable.com/blog/kafka-avro-schema-registry/index.html

那麼在RTDP架構中,如何解決Kafka訊息元資料管理和模式演變問題呢?

1.2.2.1 元資料管理(Metadata Management)
  • DBus會自動將實時感知的資料庫元資料變化記錄下來並提供服務
  • DBus會自動將線上格式化的日誌元資料資訊記錄下來並提供服務
  • DBus會發布在Kafka上釋出統一UMS訊息,UMS本身自帶訊息元資料資訊,因此下游消費時無需呼叫中心化元資料服務,可以直接從UMS訊息裡拿到資料的元資料資訊
1.2.2.2 模式演變(Schema Evolution)
  • UMS訊息會自帶Schema的Namespace資訊,Namespace是一個7層定位字串,可以唯一定位任何表的任何生命週期,相當於資料表的IP地址,形式如下:

[Datastore].[Datastore Instance].[Database].[Table].[TableVersion].[Database Partition].[Table Partition]

例:oracle.oracle01.db1.table1.v2.dbpar01.tablepar01

其中[Table Version]代表了這張表的某個Schema的版本號,如果資料來源是資料庫,那麼這個版本號是由DBus自動維護的。

  • 在RTDP架構中,Kafka的下游是由Wormhole消費的,Wormhole在消費UMS時,會將[TableVersion]作為*處理,意味著當某表上游Schema變更時,Version會自動升號,但Wormhole會無視這個Version變化,將會消費此表所有版本的增量/全量資料,那麼Wormhole如何做到相容性模式演變支援呢?在Wormhole裡可以配置流上處理SQL和輸出欄位,當上遊Schema變更是一種“相容性變更”(指增加欄位,或者修改擴大欄位型別等)時,是不會影響到Wormhole SQL正確執行的。當上遊發生非相容性變更時,Wormhole會報錯,這時就需要人工介入對新Schema的邏輯進行修復。

由上文可以看出,Schema Registry和DBus+UMS是兩種不同的解決元資料管理和模式演變的設計思路,兩者各有優勢和劣勢,可以參考表1的簡單比較。

表1 Schema Registry 與 DBus+UMS 對比 

這裡給出一個UMS的例子:

圖6 UMS訊息舉例

1.2.3 流式處理平臺Wormhole

圖7 RTDP架構之Wormhole

1.2.3.1 Wormhole設計思想

1)從外部角度看待設計思想

  • 消費來自Kafka 的UMS訊息和自定義JSON訊息
  • 負責對接不同的資料目標儲存 (Sink),並通過冪等邏輯實現Sink的最終一致性
  • 支援配置SQL方式實現流上處理邏輯
  • 提供Flow抽象。Flow由一個Source Namespace和一個Sink Namespace定義,且具備唯一性。Flow上可以定義處理邏輯,是一種流上處理的邏輯抽象,通過與物理Spark Streaming、Flink Streaming解耦,使得同一個Stream可以處理多個Flow處理流,且Flow可以在不同Stream上任意切換。
  • 支援基於回灌(backfill)的Kappa架構;支援基於Wormhole Job的Lambda架構

2)從內部角度看待設計思想

  • 基於Spark Streaming、Flink計算引擎進行資料流上處理。Spark Streaming可支援高吞吐、批量Lookup、批量寫Sink等場景;Flink可支援低延遲、CEP規則等場景。
  • 通過ums_id_, ums_op_實現不同Sink的冪等入庫邏輯
  • 通過計算下推實現Lookup邏輯優化
  • 抽象幾個統一以支援功能靈活性和設計一致性

✔ 統一DAG高階分形抽象

✔ 統一通用流訊息UMS協議抽象

✔ 統一資料邏輯表名稱空間Namespace抽象

  • 抽象幾個介面以支援可擴充套件性

✔ SinkProcessor:擴充套件更多Sink支援

✔ SwiftsInterface:自定義流上處理邏輯支援

✔ UDF:更多流上處理UDF支援

  • 通過Feedback訊息實時歸集流式作業動態指標和統計
1.2.3.2 Wormhole功能特性
  • 支援視覺化,配置化,SQL化開發實施流式專案
  • 支援指令式動態流式處理的管理、運維、診斷和監控
  • 支援統一結構化UMS訊息和自定義半結構化JSON訊息
  • 支援處理增刪改三態事件訊息流
  • 支援單個物理流同時並行處理多個邏輯業務流
  • 支援流上Lookup Anywhere,Pushdown Anywhere
  • 支援基於業務策略的事件時間戳流式處理
  • 支援UDF的註冊管理和動態載入
  • 支援多目標資料系統的併發冪等入庫
  • 支援多級基於增量訊息的資料質量管理
  • 支援基於增量訊息的流式處理和批量處理
  • 支援Lambda架構和Kappa架構
  • 支援與三方系統無縫整合,可作為三方系統的流控引擎
  • 支援私有云部署,安全許可權管控和多租戶資源管理
1.2.3.3 Wormhole技術架構

圖8 Wormhole資料流轉架構圖

更多Wormhole技術細節和使用者介面,可以參看:

GitHub:https://github.com/edp963/wormhole

1.2.4 常用資料計算儲存選型

RTDP架構對待資料計算儲存選型的選擇採取開放整合的態度。不同資料系統有各自的優勢和適合的場景,但並沒有一個數據系統可以適合各種各樣的儲存計算場景。因此當有合適的、成熟的、主流的資料系統出現,Wormhole和Moonbox會按照需要相應的擴充套件整合支援。

這裡大致列舉一些比較通用的選型:

  • 關係型資料庫(Oracle/MySQL等):適合小資料量的複雜關係計算

  • 分散式列儲存系統

✔ Kudu:Scan優化,適合OLAP分析計算場景

✔ HBase:隨機讀寫,適合提供資料服務場景

✔ Cassandra:高效能寫,適合海量資料高頻寫入場景

✔ ClickHouse:高效能運算,適合只有insert寫入場景(後期將支援更新刪除操作)

  • 分散式檔案系統

✔ HDFS/Parquet/Hive:append only,適合海量資料批量計算場景

  • 分散式文件系統

✔ MongoDB:平衡能力,適合大資料量中等複雜計算

  • 分散式索引系統

✔ ElasticSearch:索引能力,適合做模糊查詢和OLAP分析場景

  • 分散式預計算系統

✔ Druid/Kylin:預計算能力,適合高效能OLAP分析場景

1.2.5 計算服務平臺Moonbox

圖9 RTDP架構之Moonbox

1.2.5.1 Moonbox設計思想

1)從外部角度看待設計思想

  • 負責對接不同的資料系統,支援統一方式跨異構資料系統即席混算
  • 提供三種Client呼叫方式:RESTful服務、JDBC連線、ODBC連線
  • 統一元資料收口;統一查詢語言SQL收口;統一許可權控制收口
  • 提供兩種查詢結果寫出模式:Merge、Replace
  • 提供兩種互動模式:Batch模式、Adhoc模式
  • 資料虛擬化實現,多租戶實現,可看作是虛擬資料庫

2)從內部角度看待設計思想

  • 對SQL進行解析,經過常規Catalyst處理解析流程,最終生成可下推資料系統的邏輯執行子樹進行下推計算,然後將結果拉回進行混算並返回
  • 支援兩層Namespace:database.table,以提供虛擬資料庫體驗
  • 提供分散式服務模組Moonbox Grid提供高可用高併發能力
  • 對可全部下推邏輯(無混算)提供快速執行通道
1.2.5.2 Moonbox功能特性
  • 支援跨異構系統無縫混算
  • 支援統一SQL語法查詢計算和寫入
  • 支援三種呼叫方式:RESTful服務、JDBC連線、ODBC連線
  • 支援兩種互動模式:Batch模式、Adhoc模式
  • 支援Cli Command工具和Zeppelin
  • 支援多租戶使用者許可權體系
  • 支援表級許可權、列級許可權、讀許可權、寫許可權、UDF許可權
  • 支援YARN排程器資源管理
  • 支援元資料服務
  • 支援定時任務
  • 支援安全策略
1.2.5.3 Moonbox技術架構

圖10 Moonbox邏輯模組

更多Moonbox技術細節和使用者介面,可以參看:

GitHub: https://github.com/edp963/moonbox

1.2.6 可視應用平臺Davinci

圖11 RTDP架構之Davinci

1.2.6.1 Davinci設計思想

1)從外部角度看待設計思想

  • 負責各種資料視覺化展示功能
  • 支援JDBC資料來源
  • 提供平權使用者體系,每個使用者可以建立屬於自己的Org、Team和Project
  • 支援SQL編寫資料處理邏輯,支援拖拽式編輯視覺化展示,提供多使用者社交化分工協作環境
  • 提供多種不同的圖表互動能力和定製化能力,以應對不同資料視覺化需求
  • 提供嵌入整合進其他資料應用的能力

2)從內部角度看待設計思想

  • 圍繞View和Widget展開。View是資料的邏輯檢視;Widget是資料視覺化檢視
  • 通過使用者自定義選擇分類資料、有序資料和量化資料,按照合理的視覺化邏輯自動展現檢視
1.2.6.2 Davinci功能特性

1)資料來源

  • 支援JDBC資料來源
  • 支援CSV檔案上傳

2)資料檢視

  • 支援定義SQL模版
  • 支援SQL高亮顯示
  • 支援SQL測試
  • 支援回寫操作

3)可視元件

  • 支援預定義圖表
  • 支援控制器元件
  • 支援自由樣式

4)互動能力

  • 支援可視元件全屏顯示
  • 支援可視元件本地控制器
  • 支援可視元件間過濾聯動
  • 支援群控控制器可視元件
  • 支援可視元件本地高階過濾器
  • 支援大資料量展示分頁和滑塊

5)整合能力

  • 支援可視元件CSV下載
  • 支援可視元件公共分享
  • 支援可視元件授權分享
  • 支援儀表板公共分享
  • 支援儀表板授權分享

6)安全許可權

  • 支援資料行列許可權
  • 支援LDAP登入整合

更多Davinci技術細節和使用者介面,可以參看:

GitHub:https://github.com/edp963/davinci

1.3 切面話題討論

1.3.1 資料管理

1)元資料管理

  • DBus可以實時拿到資料來源的元資料並提供服務查詢
  • Moonbox可以實時拿到資料系統的元資料並提供服務查詢
  • 對於RTDP架構來說,實時資料來源和即席資料來源的元資料資訊可以通過呼叫DBus和Moonbox的RESTful服務歸集,可以基於此建設企業級元資料管理系統

2)資料質量

  • Wormhole可以配置訊息實時落入HDFS(hdfslog)。基於hdfslog的Wormhole Job支援Lambda架構;基於hdfslog的Backfill支援Kappa架構。可以通過設定定時任務選擇Lambda架構或者Kappa架構對Sink進行定時重新整理,以確保資料的最終一致性。Wormhole還支援將流上處理異常或Sink寫入異常的訊息資訊實時Feedback到Wormhole系統中,並提供RESTful服務供三方應用呼叫處理。
  • Moonbox可以對異構系統進行即席混算,這個能力賦予Moonbox“瑞士軍刀”般的便利性。可以通過Moonbox編寫定時SQL指令碼邏輯,對關注的異構系統資料進行比對,或對關注的資料表字段進行統計等,可以基於Moonbox的能力二次開發資料質量檢測系統。

3)血緣分析

  • Wormhole的流上處理邏輯通常SQL即可滿足,這些SQL可以通過RESTful服務進行歸集。
  • Moonbox掌管了資料查詢的統一入口,並且所有邏輯均為SQL,這些SQL可以通過Moonbox日誌進行歸集。
  • 對於RTDP架構來說,實時處理邏輯和即席處理邏輯的SQL可以通過呼叫Wormhole的RESTful服務和Moonbox的日誌歸集,可以基於此建設企業級血緣分析系統。

1.3.2 資料安全

圖12 RTDP資料安全

上圖給出了RTDP架構中,四個開源平臺覆蓋了端到端資料流轉鏈路,並且在每個節點上都有對資料安全各個方面的考量和支援,確保了實時資料管道端到端的資料安全性。

另外,由於Moonbox成為了面向應用層資料訪問的統一入口,因此基於Moonbox的操作審計日誌可以獲得很多安全層面的資訊,可以圍繞操作審計日誌建立資料安全預警機制,進而建設企業級資料安全系統。

1.3.3 開發運維

1)運維管理

  • 實時資料處理的運維管理向來是個痛點,DBus和Wormhole通過視覺化UI提供了視覺化運維管理能力,讓人工運維變得簡單。
  • DBus和Wormhole提供了健康檢查、操作管理、Backfill、Flow漂移等RESTful服務,可以基於此研發自動化運維繫統。

2)監控預警

  • DBus和Wormhole均提供視覺化監控介面,可以實時看到邏輯表級的吞吐和延遲等資訊。
  • DBus和Wormhole提供了心跳、Stats、狀態等RESTful服務,可以基於此研發自動化預警系統。

二、模式場景探討

上一章我們介紹了RTDP架構各個技術元件的設計架構和功能特性,至此讀者已經對RTDP架構如何落地有了具體的認識和了解。那麼RTDP架構可以解決哪些常見資料應用場景呢?下面我們會探討幾種使用模式,以及不同模式適應何種需求場景。

2.1 同步模式

2.1.1 模式描述

同步模式,是指只配置異構資料系統之間的資料實時同步,在流上不做任何處理邏輯的使用模式。

具體而言,通過配置DBus將資料從資料來源實時抽取出來投放在Kafka上,然後通過配置Wormhole將Kafka上資料實時寫入到Sink儲存中。同步模式主要提供了兩個能力:

  • 後續資料處理邏輯不再執行在業務備庫上,減少了對業務備庫的使用壓力
  • 提供了將不同物理業務備庫資料實時同步到同一物理資料儲存的可能性

2.1.2 技術難點

具體實施比較簡單。

IT實施人員無需瞭解太多流式處理的常見問題,不需要考慮流上處理邏輯實現的設計和實施,只需要瞭解基本的流控引數配置即可。

2.1.3 運維管理

運維管理比較簡單。

需要人工運維。但由於流上沒有處理邏輯,因此容易把控流速,無需考慮流上處理邏輯本身的功耗,可以給出一個相對穩定的同步管道配置。並且也很容易做到定時端到端資料比對來確保資料質量,因為源端和目標端的資料是完全一致的。

2.1.4 適用場景

  • 跨部門資料實時同步共享
  • 交易資料庫和分析資料庫解耦
  • 支援數倉實時ODS層建設
  • 使用者自助實時簡單報表開發
  • 等等

2.2 流算模式

2.2.1 模式描述

流算模式,是指在同步模式的基礎上,在流上配置處理邏輯的使用模式。

在RTDP架構中,流上處理邏輯的配置和支援主要在Wormhole平臺上進行。在同步模式的能力之上,流算模式主要提供了兩個能力:

  • 流上計算將批量計算集中功耗分散在流上增量計算持續功耗,極大降低了結果快照的時間延遲
  • 流上計算提供了跨異構系統混算的新的計算入口(Lookup)

2.2.2 技術難點

具體實施相對較難。

使用者需要了解流上處理能做哪些事,適合做哪些事,如何轉化全量計算邏輯成為增量計算邏輯等。還要考慮流上處理邏輯本身功耗和依賴的外部資料系統等因素來調節配置更多引數。

2.2.3 運維管理

運維管理相對較難。

需要人工運維。但比同步模式運維管理更難,主要體現在流控引數配置考慮因素較多、無法支援端到端資料比對、要選擇結果快照最終一致性實現策略、要考慮流上Lookup時間對齊策略等方面問題。

2.2.4 適用場景

  • 對低延遲要求較高的資料應用專案或報表
  • 需要低延遲呼叫外部服務(如流上呼叫外部規則引擎、線上演算法模型使用等)
  • 支援數倉實時事實表+維度表的寬表建設
  • 實時多表融合、分拆、清洗、標準化Mapping場景
  • 等等

2.3 輪轉模式

2.3.1 模式描述

輪轉模式,是指在流算模式的基礎上,在資料實時落庫中,同時跑短時定時任務在庫上進一步計算後,將結果再次投放在Kafka上跑下一輪流上計算,這樣流算轉批算、批算轉流算的使用模式。

在RTDP架構中,可以利用Kafka->Wormhole->Sink->Moonbox->Kafka的整合方式實現任何輪次任何頻次的輪轉計算。在流算模式的能力之上,輪轉模式提供的主要能力是:理論上支援低延遲的任何複雜流轉計算邏輯。

2.3.2 技術難點

具體實施難。

Moonbox轉Wormhole能力的引入,比流算模式進一步增加了考慮的變數因素,如多Sink的選擇、Moonbox計算的頻率設定、如何拆分Wormhole和Moonbox的計算分工等方面問題。

2.3.3 運維管理

運維管理難。

需要人工運維。和流算模式比,需要更多資料系統因素的考慮、更多引數的配置調優、更難的資料質量管理和診斷監控。

2.3.4 適用場景

  • 低延遲的多步驟的複雜資料處理邏輯場景
  • 公司級實時資料流轉處理網路建設

2.4 智慧模式

2.4.1 模式描述

智慧模式,是指利用規則或演算法模型來進行優化和增效的使用模式。

可以智慧化的點:

  • Wormhole Flow的智慧漂移(智慧化自動化運維)
  • Moonbox預計算的智慧優化(智慧化自動化調優)
  • 全量計算邏輯智慧轉換成流式計算邏輯,然後部署在Wormhole + Moonbox(智慧化自動化開發部署)
  • 等等

2.4.2 技術難點

具體實施在理論上最簡單,但有效的技術實現最難。

使用者只需要完成離線邏輯開發,剩下交由智慧化工具完成開發、部署、調優、運維。

2.4.3 運維管理

零運維。

2.4.4 適用場景

全場景。 

自此,我們對“如何設計實時資料平臺”這個話題的討論暫時告一段落。我們從概念背景,討論到架構設計,接著介紹了技術元件,最後探討了模式場景。由於這裡涉及到的每個話題點都很大,本文只是做了淺層的介紹和探討。後續我們會不定期針對某個具體話題點展開詳細討論,將我們的實踐和心得呈現出來,拋磚引玉,集思廣益。如果對RTDP架構中的四個開源平臺感興趣,歡迎在GitHub上找到我們,瞭解使用,交流建議。

作者:盧山巍

來源:宜信技術