作者:張學程
本文為 DM 原始碼閱讀系列文章的第二篇,第一篇文章 簡單介紹了 DM 原始碼閱讀的目的和規劃,以及 DM 的原始碼結構以及工具鏈。從本篇文章開始,我們會正式開始閱讀 DM 的原始碼。
本篇文章主要介紹 DM 的整體架構,包括 DM 有哪些元件、各元件分別實現什麼功能、元件之間互動的資料模型和 RPC 實現。
整體架構
通過上面的 DM 架構圖,我們可以看出,除上下游資料庫及 Prometheus 監控元件外,DM 自身有 DM-master、DM-worker 及 dmctl 這 3 個元件。其中,DM-master 負責管理和排程資料同步任務的各項操作,DM-worker 負責執行具體的資料同步任務,dmctl 提供用於管理 DM 叢集與資料同步任務的各項命令。
DM-master
DM-master 的入口程式碼在 cmd/dm-master/main.go
,其中主要操作包括:
-
呼叫
cfg.Parse
解析命令列引數與引數配置檔案 -
呼叫
log.SetLevelByString
設定程式的 log 輸出級別 -
呼叫
signal.Notify
註冊系統 signal 通知,用於接受到指定訊號時退出程式等 -
呼叫
server.Start
啟動 RPC server,用於響應來自 dmctl 與 DM-worker 的請求
在上面的操作中,可以看出其中最關鍵的是步驟 4,其對應的實現程式碼在 dm/master/server.go
中,其核心為 Server
這個 struct,其中的主要 fields 包括:
-
rootLis, svr
:監聽網路連線,分發 RPC 請求給對應的 handler。 -
workerClients
:維護叢集各 DM-worker ID 到對應的 RPC client 的對映關係。 -
taskWorkers
:維護用於執行各同步(子)任務的 DM-worker ID 列表。 -
lockKeeper
:管理在協調處理 sharding DDL 時的 lock 資訊。 -
sqlOperatorHolder
:管理手動 skip/replace 指定 sharding DDL 時的 SQL operator 資訊。
在本篇文章中,我們暫時不會關注 lockKeeper
與 sqlOperatorHolder
,其具體的功能與程式碼實現會在後續相關文章中進行介紹。
在 DM-master Server 的入口方法 Start
中:
-
通過
net.Listen
初始化rootLis
並用於監聽 TCP 連線(藉助 soheilhy/cmux,我們在同一個 port 同時提供 gRPC 與 HTTP 服務)。 -
根據讀取的配置資訊(
DeployMap
),初始化用於連線到各 DM-worker 的 RPC client 並儲存在workerClients
中。 -
通過
pb.RegisterMasterServer
註冊 gRPC server(svr
),並將該Server
作為各 services 的 implementation。 -
呼叫
m.Serve
開始提供服務。
DM-master 提供的 RPC 服務包括 DM 叢集管理、同步任務管理等,對應的 service 以 Protocol Buffers 格式定義在 dm/proto/dmmaster.proto
中,對應的 generated 程式碼在 dm/pb/dmmaster.pb.go
中。各 service 的具體實現在 dm/master/server.go
中(*Server
)。
DM-worker
DM-worker 的結構與 DM-master 類似,其入口程式碼在 cmd/dm-worker/main.go
中。各 RPC services 的 Protocol Buffers 格式定義在 dm/proto/dmworker.proto
中,對應的 generated 程式碼在 dm/pb/dmworker.pb.go
中,對應的實現程式碼在 dm/worker/server.go
中(*Server
)。DM-worker 的啟動流程與 DM-master 類似,在此不再額外說明。
Server
這個 struct 的主要 fields 除用於處理 RPC 的 rootLis
與 svr
外,另一個是用於管理同步任務與 relay log 的 worker
(相關程式碼在 dm/worker/worker.go
中)。
在 Worker
這個 struct 中,主要 fields 包括:
-
subTasks
:維護該 DM-worker 上的所有同步子任務資訊。 -
relayHolder
:對 relay 處理單元相關操作進行簡單封裝,轉發相關操作請求給 relay 處理單元,獲取 relay 處理單元的狀態資訊。 -
relayPurger
:根據使用者配置及相關策略,嘗試定期對 relay log 進行 purge 操作。
資料同步子任務管理的程式碼實現主要在 dm/worker/subtask.go
中, relay 處理單元管理的程式碼實現主要在 dm/worker/relay.go
中,對 relay log 進行 purge 操作的程式碼實現主要在 relay/purger
pkg 中。在本篇文章中,我們暫時只關注 DM 架構相關的實現,上述各功能的具體實現將在後續的相關文章中展開介紹。
Worker
的入口方法為 Start
,其中的主要操作包括:
-
通過
w.relayHolder.Start
啟動 relay 處理單元,開始從上游拉取 binlog。 -
通過
w.relayPurger.Start
啟動後臺 purge 執行緒,嘗試對 relay log 進行定期 purge。
其他的操作主要還包括處理 Server
轉發而來的同步任務管理、relay 處理單元管理、狀態資訊查詢等。
dmctl
dmctl 的入口程式碼在 cmd/dm-ctl/main.go
,其操作除引數解析與 signal 處理外,主要為呼叫 loop
進入命令處理迴圈、等待使用者輸入操作命令。
在 loop
中,我們藉助 chzyer/readline 提供命令列互動環境,讀取使用者輸入的命令並輸出命令執行結果。一個命令的處理流程為:
-
呼叫
l.Readline
讀取使用者輸入的命令 -
判斷是否需要退出命令列互動環境(exit 命令)或需要進行處理
-
呼叫
ctl.Start
進行命令分發與處理
dmctl 的具體命令處理實現在 dm/ctl
pkg 中,入口為 dm/ctl/ctl.go
中的 Start
方法,命令的分發與引數解析藉助於 spf13/cobra。命令的具體功能實現在相應的子 pkg 中:
-
master
:dmctl 與 DM-master 互動的命令,是當前 DM 推薦的命令互動方式。 -
worker
:dmctl 與 DM-worker 互動的命令,主要用於開發過程中進行 debug,當前並沒有實現所有 DM-worker 支援的命令,未來可能廢棄。 -
common
:多個命令依賴的通用操作及 dmctl 依賴的配置資訊等。
每個 dmctl 命令,其主要對應的實現包括 3 個部分:
-
在各命令對應的實現原始檔中,通過
New***Cmd
形式的方法建立cobra.Command
物件。 -
在
dm/ctl/ctl.go
中通過呼叫rootCmd.AddCommand
新增該命令。 -
在各命令對應的實現原始檔中,通過
***Func
形式的方法實現引數驗證、RPC 呼叫等具體功能。
任務管理呼叫鏈示例
讓我們用一個啟動資料同步任務的操作示例來說明 DM 中的元件互動與 RPC 呼叫流程。
-
使用者在 dmctl 命令列互動環境中輸入 start-task 命令及相應引數。
-
dmctl 在
dm/ctl/ctl.go
的Start
方法中進行命令分發,請求dm/ctl/master/start_task.go
中的startTaskFunc
處理命令。 -
startTaskFunc
通過cli.StartTask
呼叫 DM-master 上的 RPC 方法。 -
DM-master 中的
Server.StartTask
方法(dm/master/server.go
)響應來自 dmctl 的 RPC 請求。 -
Server.Start
從workerClients
中獲取任務對應 DM-worker 的 RPC client,並通過cli.StartSubTask
呼叫 DM-worker 上的 RPC 方法。 -
DM-worker 中的
Server.StartSubTask
方法(dm/worker/server.go
)響應來自 DM-master 的 RPC 請求。 -
Server.StartSubTask
中將任務管理請求轉發給Worker.StartSubTask
(dm/worker/worker.go
),並將處理結果通過 RPC 返回給 DM-master。 -
DM-master 將 DM-worker 返回的 RPC 響應重新封裝後通過 RPC 返回給 dmctl。
-
dmctl 通過
common.PrettyPrintResponse
輸出命令操作的 RPC 響應。
小結
在本篇文章中,我們主要介紹了 DM 的各個元件的入口函式,最後以 dmctl 的 start-task 為例介紹了互動的呼叫流程細節。下一篇文章我們會開始介紹 DM-worker 元件內各資料同步處理單元(relay-unit, dump-unit, load-unit, sync-unit)的設計原理與具體實現。