简介:Canal 是阿里巴巴开源的一个基于 MySQL 数据库增量日志解析的中间件,用于提供准实时的数据同步功能。
,需要先开启 Binlog 写入功能,配置 binlog-format 为 ROW 模式,my.cnf 中配置如下:
- [mysqld]
- log-bin=mysql-bin # 开启 binlog
- binlog-format=ROW # 选择 ROW 模式
- server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
canal 链接 MySQL 账号具有作为 MySQL slave 的权限, 如果已有账户可直接 grant
- CREATE USER canal IDENTIFIED BY 'canal';
- GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
- -- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
- FLUSH PRIVILEGES;
首先从 GitHub 上下载 Canal ,解压后根据官方文档配置 Canal。
我这里下载的快照版,![]()
直接在windows上解压,![]()
在lib目录下添加如下jar![]()
修改 conf/example/instance.properties 文件,主要配置项包括 MySQL 的地址、端口、用户名、密码以及要监听的数据库和表等。
这里是本地部署,可不做修改
通过命令行启动 Canal Server,如 startup.bat example,这里的 example 是实例名称,对应配置文件夹下的配置文件名。
执行 startup.bat example 命令后,logs/canal/canal.log 打印如下日志证明启动成功;

- <dependency>
- <groupId>com.alibaba.ottergroupId>
- <artifactId>canal.clientartifactId>
- <version>1.1.4version>
- dependency>
- public static void main(String args[]) {
- // 创建链接
- CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress(AddressUtils.getHostIp(),
- 11111), "example", "canal", "canal");
- int batchSize = 1000;
- int emptyCount = 0;
- try {
- connector.connect();
- connector.subscribe(".*\\..*");
- 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();
- }
- }
-
- private static void printEntry(List
entrys) { - for (CanalEntry.Entry entry : entrys) {
- if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
- continue;
- }
-
- CanalEntry.RowChange rowChage = null;
- try {
- rowChage = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
- } catch (Exception e) {
- throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),
- e);
- }
-
- CanalEntry.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 (CanalEntry.RowData rowData : rowChage.getRowDatasList()) {
- if (eventType == CanalEntry.EventType.DELETE) {
- printColumn(rowData.getBeforeColumnsList());
- } else if (eventType == CanalEntry.EventType.INSERT) {
- printColumn(rowData.getAfterColumnsList());
- } else {
- System.out.println("------->修改前");
- printColumn(rowData.getBeforeColumnsList());
- System.out.println("-------> 修改后");
- printColumn(rowData.getAfterColumnsList());
- }
- }
- }
- }
-
- private static void printColumn(List
columns) { - for (CanalEntry.Column column : columns) {
- System.out.println(column.getName() + " : " + column.getValue() + " update=" + column.getUpdated());
- }
- }

在控制台会出现如下效果:
