本人閱讀canal源碼心得
canal用來干嘛的?
說的簡單直白點就把你的數據庫的binlog文件內容准實時傳遞給你的客戶端,有了數據還不是想干嘛就干嘛。
它的大致框架是什么呢?
如果leader提出設計canal這樣的需求,腦海中肯定浮現這樣一個類CS的系統架構,其中S端就用來處理一些跟mysql,binlog以及其他一些雜七雜八的事,C端最多把接收的數據封裝下,做下簡單處理。至於怎么通信呢?那肯定首選Netty了。有個大致框架,接來下就是進一步細化。
客戶端、服務端的的信息管理。
既然有客戶端和服務端,那肯定要管理下這些元信息,信息管理無非就是把信息找個地方存儲下,在目前框架中常見的就這幾種:本地內存存儲,本地文件存儲,其他第三方存儲容器。在canal里面采用本地內存+zookeeper存儲方式。zookeeper中創建了如下幾個節點(針對服務端):
1; /otter/canal/cluster.其中cluster的child節點分別存儲的是各個S端的server信息,節點名稱ip:port,屬性:臨時節點,
2;/otter/canal/destinations/{destination}/cluster/{ip:port} 注冊實例信息
3;/otter/canal/destinations/{destiantion}/running/ 存儲的是正在運行的節點信息
既然有了這些節點那肯定要在在節點上設置監聽器,監聽器主要是處理節點丟失和節點信息有變的情況。對於注冊實例信息的節點,監聽器主要是在session斷連的情況下重新注冊信息。對於running節點信息,監聽器主要處理節點內容改變和節點刪除,節點內容有變(例如主動下線某個實例)時,則Server端發起stop non-active 的實例操作,若節點刪除了,則主動啟動實例,並注冊信息到running節點。
如何取出binlog的記錄並存儲
客戶端和服務端的metadata信息處理完了,接下來處理的是如何把binlog的信息fetch出來,存儲在本地。canal對此抽象出了三個組件:EventParser,EventSink,EventStore和MetaManager。其中MetaManager記錄的是客戶端的消費信息,主要為三種:destinations——Map數據結構,key為server端實例名稱,value為訂閱了該實例的消費客戶端;cursors——Map數據結構,key為客戶端的抽象類ClientIdentity,value為消費的位置信息;batches——記錄消費的批次信息。MetaManager里面的信息會同步到Zookeeper中(針對ZookeeperMetaManager),其中zookeeper的對應的存儲節點分別為:
1,destiantions:/otter/canal/destinations/{destination}
2,cursor:/otter/canal/destiantions/{destination}/cursor/{clientId}
3,batches:/otter/canal/destiantions/{destination}/mark/{clientId}/{batchId},
一份信息兩個地方存儲,為了保證數據的一致性,啟動了一個線程,定時更新本地內存的cursor信息到遠程zookeeper中。
EventSink類似於過濾器的作用,針對獲取到的Bin log進行過濾。EventSink主要包含Handers(List數據結構),一些固定的過濾性配置:filterTransactionEntry(是否需要過濾事務頭和尾部),filterEmptyTransactionEntry(是否需要過濾空事務頭和尾部),一個過濾器filter。EventSink里面的組件功從組件的名字就能看出來了。
其中EventStore用於存儲Bin log的記錄,主要數據結構是數組和相關的位置index屬性,通過一個數組和index屬性(最新put的位置在哪,消費到哪了),構建了一個環形的隊列。
其中EventParser主要兩個功能,一個是按需fetch bin log記錄,一個是解析bin log記錄轉化成CanalEntry。其中Bin log的位置獲取,主要是通過查詢 show master status 和show binlog events limit 1,支持查找最新的和查找某個時刻后的。當然在這個過程中還需要考慮種種異常情況,比如文件找不到怎么辦,發生了准備切換怎么辦(回退到fallbackIntervalInSeconds之前,按時間來fetch)。
EventParser,EventStore,EventSink之間如何協調工作?
如下面時序圖所示(來源:https://www.jianshu.com/p/b50cbb7254b8):
如何接受客戶端的請求
S端的架構設計好了,接下來設計C端與S端的通信。Canal中的連接部分是基於Netty,通過在ChannelPipeline中添加FixedHeaderFrameDecoder,HandshakeInitializationHandler,ClientAuthenticationHandler,SessionHandler,從名稱看也能猜出FixedHeaderFrameDecoder用於編碼解碼,HandshakeInitializationHandler用於處理請求連接的動作,ClientAuthenticationHandler那肯定是校驗客戶端了,核心的處理內容在SessionHandler。其中SessionHandler主要處理客戶端的5中指令:SUBSCRIPTION,UNSUBSCRIPTION,GET,CLIENTACK,CLIENTROLLBACK。針對SUBSCRIPTION請求,Server記錄客戶端要訂閱實例信息,以及客戶端的過濾器信息。針對GET請求,Server從EventStore中獲取Bin log記錄信息,針對ACK請求, Server更新客戶端的消費記錄,針對CLIENTROLLBACK請求 ,客戶端端消費失敗了,Server將該客戶端的消費進度回滾。
客戶端
客戶端設計時要解決得首要問題就是采用什么方式來消費Bin log記錄。客戶端采用的時候pull的方式,定時去拉取信息。
系統框架圖