Canalv1.1.4版本搭建HA叢集
阿新 • • 發佈:2020-09-05
## 前提
`Canal`上一個正式版是於`2019-9-2`釋出的`v1.1.4`,筆者幾個月前把這個版本的`Canal`推上了生產環境,部署了`HA`叢集。過程中雖然遇到不少的坑,但是在不出問題的前提下,`Canal`的作用還是非常明顯的。上週的一次改造上線之後,去掉了原來對業務系統訂單資料通過`RabbitMQ`實時推送的依賴,下游的統計服務完全通過上游業務主庫的`binlog`事件進行聚合,從而實現了核心業務和實時統計兩個不同的模組解耦。
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-1.png)
這篇文章簡單分析一下如何搭建生產環境下可靠的`Canal`高可用叢集。
## Canal高可用叢集架構
`Canal`的`HA`其實包含了服務端`HA`和客戶端的`HA`,兩者的實現原理差不多,都是通過`Zookeeper`例項標識某個特定路徑下搶佔`EPHEMERAL`(臨時)節點的方式進行控制,搶佔成功的一者會作為執行節點(狀態為`running`),而搶佔失敗的一方會作為備用節點(狀態是`standby`)。下文只分析服務端`HA`叢集搭建,因為一般情況下使用內建的資料管道例如`Kafka`,基本遮蔽了客戶端的細節。假設客戶端使用了`Kafka`,也就是`Canal`從主庫同步到的`binlog`事件最終會投放到`Kafka`,那麼`Canal`服務端`HA`叢集架構大致如下:
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-2.png)
這是全域性來看,一個執行的`Canal`服務端,可以同時支援監聽多個上游資料庫的`binlog`,某個主庫解析配置的抽象在`Canal`中的術語叫做`Instance`(例項):
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-3.png)
定義多個`Instance`的操作很簡單,主配置檔案`$CANAL_HOME/conf/canal.properties`中的`canal.destinations`配置項通過英文逗號分隔多個標識如:
```shell
# canal.destinations=[Instance標識1,Instance標識2...,Instance標識n]
canal.destinations=customer-service,payment-service
```
然後在`$CANAL_HOME/conf`目錄下新增`customer-service`和`payment-service`資料夾,把原來的`$CANAL_HOME/conf/example`資料夾中的`instance.properties`拷貝過去,按需修改裡面的配置即可:
```shell
$CANAL_HOME
- conf
- customer-service
- instance.properties # 這裡主要配置customer-service主庫的連線資訊、過濾規則和目標topic的配置等等
配置 【canal.mq.topic = customer-service】
- payment-service
- instance.properties # 這裡主要配置payment-service主庫的連線資訊和過濾規則和目標topic的配置等等
配置 【canal.mq.topic = payment-service】
```
而`Canal`最終解析好的`binlog`事件會分別以`topic`為`customer-service`或`payment-service`傳送到`Kafka`叢集中,這樣就能確保不同資料來源解析出來的`binlog`不會混亂。
> Canal會實時監聽每個Instance的配置檔案instance.properties的變動,一旦發現配置檔案有屬性項變更,會進行一次熱載入,原則是變更Instance的配置檔案是不用重啟Canal服務的。
## 搭建Canal高可用叢集
> 為了簡單起見,Zookeeper和Kafka使用單節點作為示例,實際上生產環境中建議Zookeeper或Kafka都使用奇數個(>=3)節點的叢集。
筆者本地一臺`CentOS7.x`的虛擬機器`192.168.56.200`上安裝了`Zookeeper`和`Kafka`,本地開發機`192.168.56.1`是`Windows10`作業系統。虛擬機器安裝了一個`MySQL8.x`的服務端(`Canal`要求`MySQL`服務開啟`binlog`支援特性,並且要求`binlog`型別為`ROW`,這兩點`MySQL8.x`是預設開啟的),現在詳細講解在這兩臺機器上搭建一個`Canal`服務端`HA`叢集。
> 生產上搭建Canal服務端HA叢集的機器最好在同一個內網中,並且建議伺服器由Canal獨佔,不要部署其他中介軟體或者應用,機器的配置建議4核心8GB記憶體起步。
下載當前(`2020-08-22`)最新版本的[canal.deployer-1.1.4.tar.gz](https://github.com/alibaba/canal/releases/download/canal-1.1.4/canal.deployer-1.1.4.tar.gz):
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-4.png)
拷貝和解壓`canal.deployer-1.1.4.tar.gz`到虛擬機器的`/data/canal`目錄下,同時解壓一份在本地開發機的磁碟中。**演示直接使用`example`標識的`Instance`**。修改虛擬機器`/data/canal/conf/example/instance.properties`:
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-5.png)
注意這裡筆者把`topic`設定為和資料庫的`schema`一致。其他細節項就不再進行展開,有興趣可以看筆者之前寫過的一篇文章[《基於Canal和Kafka實現MySQL的Binlog近實時同步》](https://throwx.cn/2020/03/07/canal-kafka-mysql-binlog-sync-guide),裡面很詳細地介紹了怎麼部署一個可用的`Canal`單機服務,包括了`MySQL`、`Zookeeper`和`Kafka`的安裝和使用。
同理,在開發機中的對應的配置檔案中新增一模一樣的配置項,但是`canal.instance.mysql.slaveId`配置項需要每個例項唯一,並且不能和主庫的`serverId`衝突,例如:
```shell
# 虛擬機器中的配置
canal.instance.mysql.slaveId=654321
# 開發機中的配置
canal.instance.mysql.slaveId=654322
```
然後修改虛擬機器`/data/canal/conf/canal.properties`配置,修改項主要包括:
|Key|Value|
|:-:|:-:|
|`canal.zkServers`|填寫`Zookeeper`叢集的`host:port`,這裡填寫`192.168.56.200:2181`|
|`canal.serverMode`|`kafka`|
|`canal.instance.global.spring.xml`|`classpath:spring/default-instance.xml`(一定要修改為此配置,基於Zookeeper的叢集管理依賴於此配置)|
|`canal.mq.servers`|填寫`Kafka`叢集的`host:port`,這裡填寫`192.168.56.200:9092`|
其他配置項可以按需修改。**對於`canal.properties`,`Canal`多個叢集節點可以完全一致,寫好一份然後拷貝使用即可**。接著可以分別啟動兩個`Canal`服務,一般來說,先啟動的節點會成為`running`節點:
- 對於`Linux`系統,可以使用命令`sh $CANAL_HOME/bin/startup.sh`啟動`Canal`。
- 對於`Windows`系統,直接掛起命令介面執行`$CANAL_HOME/bin/startup.bat`指令碼即可。
> Windows啟動如果控制檯報錯ch.qos.logback.core.LogbackException: Unexpected filename extension of file...,其實是因為指令碼中的logback配置檔案路徑佔位符的變數沒有預先設定值,見下圖:
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-7.png)
`Linux`下的啟動日誌(`example.log`):
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-6.png)
`Windows`下的啟動日誌(`canal.log`):
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-8.png)
## 測試Canal高可用叢集
先啟動虛擬機器中的`Canal`服務,再啟動本地開發機中的`Canal`服務:
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-9.png)
可見當前的`cluster`列表中包含了兩個`host:port`,而`running`節點中的資訊只包含虛擬機器的`host:port`,意味著當前執行節點時虛擬機器中的`Canal`服務,本地開發機中的`Canal`服務作為備用節點。此時可以嘗試在虛擬機器中執行`sh stop.sh`關閉`Canal`服務:
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-10.png)
可見`cluster`列表只剩下本地開發機中的`Canal`服務的`host:port`,而`running`節點中的資訊也是指向此服務資訊。至此**成功驗證了`Canal`主備模式**的切換。此時可以再驗證一下開發機中的`example.log`:
![](https://throwable-blog-1256189093.cos.ap-guangzhou.myqcloud.com/202008/c-h-a-g-11.png)
## 說說Canal儲存在Zookeeper中的資料節點
前文使用[ZooInspector](https://issues.apache.org/jira/secure/attachment/12436620/ZooInspector.zip)展示了`Canal`儲存在`Zookeeper`中的節點資訊,這裡簡單分析一下。節點樹的結構如下:
|節點路徑|描述|
|:-:|:-:|
|`/otter/canal`|根目錄|
|`/otter/canal/cluster`|`Canal`叢集節點資訊|
|`/otter/canal/destinations`|`Canal`所有`Instance`的資訊|
`/otter/canal/cluster`路徑的展開如下:
```shell
# 其實就是掛載了所有叢集節點的host:port資訊
/otter/canal/cluster
- 192.168.56.1:11111
- 172.17.0.1:11111
```
`/otter/canal/destinations`路徑會相對複雜,展開的資訊如下:
```shell
/otter/canal/destinations
- Instance標識
- running 記錄當前為此Instance提供服務狀態為running的Canal節點 [EPHEMERAL型別]
- cluster 記錄當前為此Instance提供服務的Canal叢集節點列表
- Client序號標識
- running 客戶端當前正在讀取的running節點 [EPHEMERAL型別]
- cluster 記錄當前讀取此Instance的客戶端節點列表
- cursor 記錄客戶端讀取的position資訊
# 例如
/otter/canal/destinations
- example
- running -> {"active":true,"address":"192.168.56.1:11111"}
- cluster
- 192.168.56.1:11111
- 172.17.0.1:11111
- 1001
- running
- cluster
- cursor
```
理解各個路徑存放的資訊,有利於在`Canal`叢集出現故障的時候結合日誌進行故障排查。
## 小結
`Canal`叢集已經在生產跑了一段時間,大部分的問題和坑都已經遇到過,有些問題通過了遮蔽某些開關解決,一些遺留無法解決的問題也想辦法通過預警手段人工介入處理。`Canal`的`HA`其實是比較典型的主備模式,也就是同一個時刻,只有單個`Canal`服務對單個`Instance`(`Destination`)進行處理,想了下確實好像這樣才能確保主備中繼日誌同步的基本有序,備用節點其實是完全划水不工作的(除了監聽`Zookeeper`中的路徑變更),一旦`running`節點出現故障或者宕機,備用節點就會提升為`running`節點,確保叢集的可用性。
(本文完 c-3-d e-a-202