1.前言
在最近項目開中,為了減少前台搜索對數據庫訪問壓力,入庫的物料都需要同步一份數據到ES,讓前台搜索直接訪問ES,不直接訪問數據庫獲取數據。一開始做法是代碼串行先保存到數據庫,再同步到ES。但是會有一個坑,如果兩者其一保存數據不成功,就會導致數據庫跟ES數據不一致,而且這種方式是對站點代碼是有侵入式的。搜索相關資料發現阿里的canal這個增量數據訂閱&消費的中間件可以無侵入式地有效解決該問題,canal偽造從庫拉取mysql庫每次修改binary log對象解析后,再通過MQ同步數據到ES。同時考慮系統健壯性,穩定性,所以把canal部署成高可用集群。
2. canal架構
我特定查閱官網資料幾遍,總體歸納canal架構如上幾種(如有錯誤,請各位看官指正,謝謝)。而這次功能重構,鑒於目前公司現有技術棧,決定采用架構圖③的架構進行重構。
2.1HA機制設計
canal的ha(雙機集群)分為兩部分,canal server和canal client分別有對應的ha實現:
●canal server:為了減少對mysql dump的請求,不同server上的實例(instance)要求同一時間只能有一個處於running,其他的處於standby狀態。
●canal client:為了保證有序性,一份實例(instance)同一時間只能由一個canal client進行get/ack/rollback操作,否則客戶端接收無法保證有序。
整個HA機制的控制主要是依賴了zookeeper的幾個特性,watcher和EPHEMERAL節點(和session生命周期綁定),后續我會繼續記錄zookeeper學習過程。
Canal Server HA架構原理:
流程步驟:
1.canal server要啟動某個canal instance時都先向zookeeper進行一次嘗試啟動判斷(實現:創建EPHEMERAL節點,誰創建成功就允許誰啟動)。
2.創建zookeeper節點成功后,對應的canal server就啟動對應的canal instance,沒有創建成功的canal instance就會處於standby狀態。
3.一旦zookeeper發現canal server A創建的節點消失后,立即通知其他的canal server再次進行步驟1的操作,重新選出一個canal server啟動instance。
4.canal client每次進行connect時,會首先向zookeeper詢問當前是誰啟動了canal instance,然后和其建立鏈接,一旦鏈接不可用,會重新嘗試connect。
注:canal client的方式和canal server方式類似,也是利用zookeeper的搶占EPHEMERAL節點的方式進行控制。
3.前期准備
3.1安裝zookeeper
可以參考我這篇“Zookeeper在linux環境中搭建集群”文章或者百度教程安裝。集群部署如下:
服務名稱 |
IP/域名 |
端口 |
zookeeper(slave) |
192.168.142.129 |
2181 |
zookeeper(master) |
192.168.142.130 |
2181 |
zookeeper(slave) |
192.168.142.131 |
2181 |
3.2安裝mysql
可以參考我這篇”MySQL進階篇在linux環境下安裝” 文章或者百度教程安裝。MySQL我只部署單台:
服務名稱 |
IP/域名 |
端口 |
mysql |
8.135.110.120(阿里雲) |
3306 |
用戶名:canal,密碼:qwer1234
3.3安裝mq(因為我公司是使用RabbitMQ,所以安裝的是RabbitMQ)
可以參考我這篇“RabbitMQ在Docker上安裝”文章或者百度教程安裝。RabbitMQ我只部署單台:
服務名稱 |
IP/域名 |
端口 |
rabbitmq |
8.135.110.120(阿里雲) |
5672 |
管理后台登錄賬號:admin,密碼:qwer1234
再新建一個隊列用作演示:
Virtual hosts:/
交換器(Exchanges):canal.direct.test
路由key(Routing key):canal.routingkey.test
隊列(Queues):canal.queue.test
3.4安裝canal.server
可以參考我這篇“Canal入門”(這篇文章采用就是架構圖④的架構)或者百度教程安裝。集群部署如下:
服務名稱 |
IP/域名 |
端口 |
canal.server01 |
192.168.142.129 |
11110 |
canal.server02 |
192.168.142.130 |
11110 |
兩台canal.server實例我重新拷貝一份用作供應商報價業務,具體操作如下:
cp -r /home/deng/canal/canal.deployer/conf/example /home/deng/canal/canal.deployer/conf/quote_example
實例名稱:quote_example
3.5其他依賴
JDK版本:java version "1.8.0_311"
4.啟動canal集群
部署完canal.server兩個服務后,集群想要生效,還需要同時修改兩台服務的配置重新啟動才可以,具體操作如下:
●編輯vi conf/ canal.properties文件
因為canal.server集群需要zookeeper,所以在“common argument”標題下找到canal.zkServers選項修改為zookeeper集群地址;然后把canal.serverMode選項修改為rabbitMQ類型:
canal.zkServers = 192.168.142.129:2181,192.168.142.130:2181,192.168.142.131:2181 canal.serverMode = rabbitMQ
同時canal需要MQ進行同步數據,所以在“RabbitMQ” 標題下找到rabbitmq配置進行修改:
rabbitmq.host = 8.135.110.120 rabbitmq.virtual.host = / rabbitmq.exchange = canal.direct.test rabbitmq.username = admin rabbitmq.password = qwer1234 rabbitmq.deliveryMode =
HA模式是依賴於instance name進行管理,必須都選擇default-instance.xml配置。在“destinations”標題下找到canal.instance.global.spring.xml選項進行啟用(其他兩個選項注釋):
canal.instance.global.spring.xml = classpath:spring/default-instance.xml
注:canal是允許配置多個實例(instance),假設每個canal.server服務都有相同的兩個實例(在conf目錄下分別建兩個實例文件夾:example1和example2,同時把默認實例example文件夾里的instance.properties文件拷貝一份過去),修改兩個實例canal.properties配置就能使其生效,在“destinations”標題下找到canal.destinations選項修改如下:
canal.destinations = example1, example2
●編輯vi conf/example/instance.properties文件(如果是多實例,則每個實例目錄下該文件都要修改配置,現在以canal.server01服務為例)
# mysql集群配置中的serverId概念,需要保證和當前mysql集群中id唯一 canal.instance.mysql.slaveId=129 # mysql數據庫連接地址和端口 canal.instance.master.address=8.135.110.120:3306 # mysql數據庫用戶名和密碼 canal.instance.dbUsername=canal canal.instance.dbPassword=qwer1234 # mq配置(如果是沒用到MQ,則修改為實例名稱即可) canal.mq.topic= example1 Or # mq配置(如果是用到MQ,則修改為mq路由key) canal.mq.topic=canal.routingkey.test
●啟動canal.server服務
cd /home/deng/canal/canal.deployer
sh bin/startup.sh
●登錄mysql增刪改一條數據(創建一個供應商報價supplier_quote臨時表)
INSERT ebs_material.supplier_quote (PN,Brand,StockQty) VALUES ('LM3585DT','TI',10000); UPDATE ebs_material.supplier_quote SET Brand='TS1' WHERE Id=1;
在RabbitMQ管理后台隊列上會看到這兩條語句待消費消息:
下面再來驗證下canal集群是否成功。
●驗證canal集群是否成功
首先把canal.server01停止運行(也可以停止canal.server02運行,隨便一個都可以):
sh bin/stop.sh
查看日志:
從上述截圖可以看到129虛擬機canal.server01服務已經關閉了。現在在數據庫再插入一條報價數據:
INSERT ebs_material.supplier_quote (PN,Brand,StockQty) VALUES ('BAV999','TI',10000);
再查看下RabbitMQ管理后台隊列有沒有新增一條待消費的消息:
由此可見,已經新增一條待消費的消息,證明canal集群部署成功!
5.小結
在部署集群過程中偶爾會發生canal集群沒有推送MQ消費情況,需要重新啟動canal.server服務才生效,不知道是因為canal內部集成對rabbitmq不友好還是怎么的,后續測試排查下問題。該章節主要介紹如何搭建canal高可用集群,后續我們會深入了解內部原理跟其他功能。
參考文獻:
AdminGuide
簡介