目錄
2.3.1 安裝 Canal 開源項目到本地 maven 倉庫
2.3.3 創建 CanalDataEventListerner.java :
一、Canal 介紹
1.1 Canal 概述
Canal是阿里巴巴旗下的一款開源項目,純Java開發。基於數據庫增量日誌解析,提供增量數據訂閱&消費。
簡單來說就是用來監控數據庫數據的變化,從而獲得新增數據或修改數據的項目框架,目前主要支持了MySQL。
1.2 Canal 原理
原理:
- canal 模擬 mysql slave 的交互協議,僞裝自己爲 mysql slave, 向mysql master 發送 dump 協議
- mysql master 收到 dump 請求,開始推送 binary log 給 slave(即 canal)
- canal 解析 binary log 對象(原始爲byte流)
由於 canal是基於mysql的主從模式實現的,所以必須先開啓 binlog
二、Canal 安裝和使用
2.1 mysql 準備
2.1.1 開啓 mysql 的 binlog 模式
由於 mysql 是使用 docker 部署的,進入 docker 修改 mysql 的配置
# 進入 mysql 的 docker 容器內
docker exec -it mysql /bin/bash
# 進入 mysql 對應的配置文件
cd /etc/mysql/mysql.conf.d
# 修改配置文件
vim mysqld.cnf
log-bin=/var/lib/mysql/mysql-bin # 指定日誌文件存儲的位置
server-id=12345 # 當前這個mysql數據庫的唯一標識
2.1.2 創建同步賬號
由於 Canal 要進入 mysql 讀取數據,出於安全考慮,需要對 Canal 創建用戶並賦予權限
# 創建賬號 %表示能在任意機器登錄 by 'xxx' 表示密碼爲canal
create user canal@'%' IDENTIFIED by 'canal';
# 授權 依次爲:查詢權限、主從複製權限、主從複製客戶端權限、超級權限
# *.* 表示 任意數據庫.任意表 都擁有相關權限
# 結合表示: 任意機器上以canal賬號登錄的用戶對mysql中任意數據庫和任意表都擁有上述權限
GRANT SELECT, PEPLICATION SLAVE, REPLICATION CLIENT, SUPER ON *.* TO 'canal'@'%'';
# 刷新數據庫
FLUSH PRIVILEGES;
2.1.3 重啓 mysql 容器
docker restart canal
2.2 Canal 容器安裝和使用
2.2.1 下載鏡像和安裝
# 下載鏡像
docker pull docker.io/canal/canal-server
# 安裝
docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server
#-p 端口映射
2.2.2 Canal 配置
進入 Canal 容器
# 進入 canal 容器
docker exec -it canal /bin/bash
# 進入配置文件夾
cd /home/admin/canal-server/conf
修改 Canal 的數據庫唯一標識,改成唯一的
修改同步配置
vi ./example/instance.properties
2.3 Canal 微服務搭建
2.3.1 安裝 Canal 開源項目到本地 maven 倉庫
在搭建 Canal 時使用了一個開源項目,實現了 Springboot 和 Canal 的集成。
https://github.com/chenqian56131/spring-boot-starter-canal
搭建:
找到項目目錄,使用命令安裝到本地 maven 倉庫
mvn install
2.3.2 創建微服務項目
創建 maven 項目,並加入依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- canal依賴 -->
<dependency>
<groupId>com.xpand</groupId>
<artifactId>starter-canal</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
2.3.3 創建 CanalDataEventListerner.java :
package com.changgou.canal;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.changgou.content.feign.ContentFeign;
import com.xpand.starter.canal.annotation.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* 實現mysql數據監聽
*/
@CanalEventListener
@Slf4j
public class CanalDataEventListener222 {
@Autowired
private ContentFeign contentFeign;
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* @InsertListenPoint 增加監聽
* @param eventType 當前操作的類型: 增加數據
* @param rowData 發生變更的一行數據
*/
@InsertListenPoint
public void onEventInsert(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
for (CanalEntry.Column column : rowData.getAfterColumnsList()) {
log.info("增加: 列名:" + column.getName() + "------ 變更的數據:" + column.getValue());
}
}
/**
* @UpdateListenPoint 修改監聽
* @param eventType 當前操作的類型: 增加數據
* @param rowData 發生變更的一行數據
*/
@UpdateListenPoint
public void onEventUpdate(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
log.info("===================================================================================");
for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
log.info("修改前: 列名:" + column.getName() + "------ 變更的數據:" + column.getValue());
}
log.info("===================================================================================");
for (CanalEntry.Column column : rowData.getAfterColumnsList()) {
log.info("修改後: 列名:" + column.getName() + "------ 變更的數據:" + column.getValue());
}
}
/**
* @DeleteListenPoint 刪除監聽
* @param eventType 當前操作的類型: 增加數據
* @param rowData 發生變更的一行數據
*/
@DeleteListenPoint
public void onEventDel(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
log.info("刪除前:列名:" + column.getName() + "------ 變更的數據:" + column.getValue());
}
}
/**
* @ListenPoint 自定義監聽
* @param eventType 當前操作的類型: 增加數據
* @param rowData 發生變更的一行數據
*/
@ListenPoint(
destination = "example", // 指定Canal實例的地址
schema = {"changgou_content"}, // 指定監聽的數據庫
table = {"tb_content", "tb_content_category"}, // 指定監控的表
eventType = {
CanalEntry.EventType.DELETE,
CanalEntry.EventType.UPDATE,
CanalEntry.EventType.INSERT} // 監聽類型
)
public void onEventCustomUpdate(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
log.info("自定義操作前: 列名:" + column.getName() + "------ 變更的數據:" + column.getValue());
}
for (CanalEntry.Column column : rowData.getAfterColumnsList()) {
log.info("自定義操作後: 列名:" + column.getName() + "------ 變更的數據:" + column.getValue());
}
}
}
2.3.4 新建啓動類
import com.xpand.starter.canal.annotation.EnableCanalClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* Canal微服務,監聽數據庫變化並響應
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableCanalClient
public class CanalApplication {
public static void main(String[] args) {
SpringApplication.run(CanalApplication.class, args);
}
}
2.3.5 編寫微服務配置文件
# 192.168.47.142 爲安裝了redis、canal的遠端服務器
server:
port: 18082
spring:
application:
name: canal
redis:
host: 192.168.47.142
port: 6379
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
timeout:
# 若enabled設置爲false,則請求超時交給ribbon控制
enabled: true
isolation:
strategy: SEMAPHORE
canal:
client:
instances:
example:
host: 192.168.47.142
port: 11111
2.4 測試
項目運行後,修改數據庫即可看到控制檯打印出修改的內容了。