Canal 简介 : 官网链接
准备
- mysql 开启binglog
- Canal 当前文档使用v1.1.6
官方的使用文档 QuickStart 链接
my.cnf /my.ini 增加以下配置
[mysqld]
log-bin=mysql-bin # 开启 binlog
binlog-format=ROW # 选择 ROW 模式
# 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
server_id=1
#加密方式
default_authentication_plugin=mysql_native_password
#表名存储在磁盘是小写的,但是比较的时候是不区分大小写
lower_case_table_names = 1
# 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
server_id=11
创建canal用户
如果给的权限过少大概率在使用的时候没有插入内容的权限
# 创建canal账户,密码为canal 默认配置就是这个
CREATE USER canal IDENTIFIED BY 'canal';
# 这里给予all权限,出现权限导致admin连不上无法操作问题很多,后期根据需求在调整
GRANT ALL ON *.* TO 'canal'@'%';
GRANT SELECT,INSERT,UPDATE,DELETE,ALTER,DROP, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
#刷新配置
FLUSH PRIVILEGES;
启动Canal Server
下载 canal, 访问 release 页面 , 选择需要的包下载, 如以 1.1.6 版本为例
解压
tar zxvf canal.deployer-1.1.6.tar.gz -C /home/canal/deployer
修改配置 conf/example/instance.properties
修改的不做配置只列注意地点
#数据库的链接地址
canal.instance.master.address=127.0.0.1:3306
canal.instance.mysql.slaveId=1234
# table regex 设置白名单,如果在instance.properties配置文件中进行该项配置,则在代码中不应该再配置
# connector.subscribe(".*\\..*");,如果还在代码中配置,则配置文件将会失效!!!
#canal.instance.filter.regex=.*\\..*
canal.instance.filter.regex=testDb.test
# table black regex 这里配置黑名单
# 此过滤条件只针对row模式的数据有效(ps. mixed/statement因为不解析sql,所以无法准确提取tableName进行过滤)
canal.instance.filter.black.regex=mysql\\.slave_.*
规则 | 配置 | 示例 |
---|---|---|
全库全表 | “.\…” | “.\…” |
指定库全表 | 库名…* | test…* |
单表 | 库名.表名 | test.test |
多规则组合使用 | 库名1…*,库名2.表名1,库名3.表名2 (逗号分隔) | test…*,test2.user1,test3.user2 (逗号分隔) |
注意:
过滤规则有2种方式,优先使用配置,也可以在代码中修改java程序下connector.subscribe配置的过滤正则
如果修改了canal配置的instance文件,一定不要在客户端调用CanalConnector.subscribe(“.\…”),不然等于没设置canal.instance.filter.regex
如果 设置了canal.instance.filter.regex。 可以设置 connector.subscribe(); 不填写任何配置。
修改配置 conf/canal.properties
# tcp bind ip
#将 canal.ip 改成canal所在机器的ip地址,避免无谓的问题 如果是本机执行可以不写 本地网卡如果有虚拟机网卡需要禁用,不然会找到其他网卡,导致本机获取的ip 虚拟机的网关地址找不到服务
canal.ip =
# register ip to zookeeper
canal.register.ip =
canal.port = 11111
canal.metrics.pull.port = 11112
# canal instance user/passwd
# canal.user = canal
# canal.passwd = E3619321C1A937C46A0D8BD1DAC39F93B27D4458
# canal admin config
#canal.admin.manager = 127.0.0.1:8089
canal.admin.port = 11110
canal.admin.user = admin
canal.admin.passwd = 4ACFE3202A5FF5CF467898FC58AAB1D615029441
# admin auto register
#canal.admin.register.auto = true
#canal.admin.register.cluster =
#canal.admin.register.name =
#去掉这个注释
canal.instance.parser.parallelThreadSize = 16
进入bin目录下启动
linux sh startup.sh
vi logs/example/example.log
# 没有报错基本上是成功了
windows 执行bin/statrup.bat
这里不使用canal.adapter 直接自己实现客户端
java代码如下
public static void main(String[] args) {
CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("127.0.0.1", 11111), "example", "", "");
//CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.113.133", 11111), "example", "", "");
int batchSize = 1000;
int emptyCount = 0;
try {
connector.connect();
connector.subscribe("test.wms");
connector.rollback();
int totalEmptyCount = 120;
while (emptyCount < totalEmptyCount) {
Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
long batchId = message.getId();
int size = message.getEntries().size();
if (batchId == -1 || size == 0) {
emptyCount++;
System.out.println("empty count : " + emptyCount);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
} else {
emptyCount = 0;
System.out.printf("message[batchId=%s,size=%s] \n", batchId, size);
printEntry(message.getEntries());
}
connector.ack(batchId); // 提交确认
//connector.rollback(batchId); // 处理失败, 回滚数据
}
System.out.println("empty too many times, exit");
} finally {
connector.disconnect();
}
}
/**
* 打印canal server解析binlog获得的实体类信息
*/
private static void printEntry(List<Entry> entrys) {
for (Entry entry : entrys) {
if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN || entry.getEntryType() == EntryType.TRANSACTIONEND) {
continue;
}
RowChange rowChage = null;
try {
rowChage = RowChange.parseFrom(entry.getStoreValue());
} catch (Exception e) {
throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),
e);
}
EventType eventType = rowChage.getEventType();
System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
entry.getHeader().getLogfileName(), entry.getHeader().getLogfileOffset(),
entry.getHeader().getSchemaName(), entry.getHeader().getTableName(),
eventType));
for (RowData rowData : rowChage.getRowDatasList()) {
if (eventType == EventType.DELETE) {
printColumn(rowData.getBeforeColumnsList());
} else if (eventType == EventType.INSERT) {
printColumn(rowData.getAfterColumnsList());
} else {
System.out.println("-------> before");
printColumn(rowData.getBeforeColumnsList());
System.out.println("-------> after");
printColumn(rowData.getAfterColumnsList());
}
}
}
}
private static void printColumn(List<Column> columns) {
for (Column column : columns) {
System.out.println(column.getName() + " : " + column.getValue() + " update=" + column.getUpdated());
}
}
启动链接成功
适当的修改表数据,会接收到消息 后续就自定义处理了