Canal介绍
官网:https://github.com/alibaba/canal
canal是阿里巴巴旗下的一款开源项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB).
目标
实时监控MySQL数据变化(后续可进行数据同步)。
实操——Docker搭建Canal
前置条件:准备一台阿里云ECS(系统CentOS7),并Docker安装好MySQL8.x(默认开启了bin_log且模式为ROWDATA)
docker run -it -d -p 11111:11111 --name canal-server canal/canal-server
进入该容器内部
docker exec -it canal-server /bin/bash
查看文件目录
进入canal-server并查看文件目录
重点是conf目录(涉及监听哪个数据源)
查看canal.properties文件
example文件夹是一个实例,如需监听多个实例,则配置多个。
进入example并查看目录
根据实际情况修改instance.properties配置文件
重启Canal-server
docker restart canal-server
=========================================================
实操——Java整合Canal,编写Canal客户端
创建Maven工程并配置pom.xml
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.protocol</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>
</dependencies>
创建CanalClient类
package com.lemon.canal;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import lombok.extern.slf4j.Slf4j;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Canal客户端
*
* @author Jiahai
*/
@Slf4j
public class CanalClient {
/**
* TODO canal服务器IP
*/
private static final String IP = "119.23.65.233";
/**
* canal端口
*/
private static final int PORT = 11111;
/**
* 目标
*/
private static final String DESTINATION = "example";
/**
* 用户名
*/
private static final String USER = "admin";
/**
* 密码
*/
private static final String PASSWORD = "4ACFE3202A5FF5CF467898FC58AAB1D615029441";
public static void main(String[] args) throws InterruptedException, InvalidProtocolBufferException {
// 创建单链接的客户端链接
CanalConnector canalConnector = CanalConnectors.newSingleConnector(new InetSocketAddress(IP, PORT), DESTINATION, USER, PASSWORD);
while (true) {
// 连接
canalConnector.connect();
// 客户端订阅,重复订阅时会更新对应的filter信息
// 说明:
// a. 如果本次订阅中filter信息为空,则直接使用canal server服务端配置的filter信息
// b. 如果本次订阅中filter信息不为空,目前会直接替换canal server服务端配置的filter信息,以本次提交的为准
canalConnector.subscribe("test.*");
// 获取数据,自动进行确认,该方法返回的条件:尝试拿batchSize条记录,有多少取多少,不会阻塞等待
Message message = canalConnector.get(200);
// 获取Entry集合
List<CanalEntry.Entry> entryList = message.getEntries();
if (entryList.isEmpty()) {
log.warn("=== 没有新数据 等待10秒再尝试拉取 ===");
TimeUnit.SECONDS.sleep(10);
continue;
}
// 遍历 entryList并逐条解析
for (CanalEntry.Entry entry : entryList) {
// 1、获取数据库表名
String tableName = entry.getHeader().getTableName();
// 2、获取类型
CanalEntry.EntryType entryType = entry.getEntryType();
// 3、获取传输的二进制数据
ByteString storeValue = entry.getStoreValue();
// 4、判断当前 entryType是否为 ROWDATA
if (CanalEntry.EntryType.ROWDATA.equals(entryType)) {
// 5、反序列化数据
CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(storeValue);
// 6、获取数据集
List<CanalEntry.RowData> rowDataList = rowChange.getRowDatasList();
JSONObject before = new JSONObject();
JSONObject after = new JSONObject();
// 7、遍历数据集
rowDataList.forEach(rowData -> {
List<CanalEntry.Column> beforeColumnsList = rowData.getBeforeColumnsList();
List<CanalEntry.Column> afterColumnsList = rowData.getAfterColumnsList();
beforeColumnsList.forEach(column -> before.put(column.getName(), column.getValue()));
afterColumnsList.forEach(column -> after.put(column.getName(), column.getValue()));
log.info("表名:{}, 数据before: {}", tableName, before);
log.info("表名:{}, 数据after: {}", tableName, after);
});
}
}
}
}
}
MySQL新建数据库test,并创建表t_user
效果
插入数据
INSERT INTO t_user VALUES (1, 'Lisa', 19);
继续插入数据
INSERT INTO t_user VALUES (2, 'Tom', 20);
INSERT INTO t_user VALUES (3, '灰太狼', 18);
删除数据
DELETE FROM t_user WHERE user_id = 3;
批量更新数据
UPDATE t_user SET age = 6;
The end
可以自行整合RocketMQ 或 Kafka(Stream算子)等。
例 MySQL → Canal → MQ → Flink → MQ → 消费者