Docker 安装 canal 详细步骤

一、canal 简介

canal [kə'næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费。

  • 基于日志增量订阅&消费支持的业务:
  1. 数据库镜像
  2. 数据库实时备份
  3. 多级索引 (卖家和买家各自分库索引)
  4. search build
  5. 业务cache刷新
  6. 价格变化等重要业务消息
  • 工作原理
  • canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送 dump 协议
  • MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
  • canal 解析 binary log 对象(原始为 byte 流) 。
  • 官网地址

Home · alibaba/canal Wiki · GitHub

二、docker 安装 Mysql 服务

2.1 创建 docker network 网络

bash 复制代码
docker network create canal_net

新建的 docker 容器Mysql、canal-admin、canal-server 都统一使用 canal-net 网络进行通信,彼此访问时可以通过容器名直接访问,不用通过 IP 地址。

2.1 docker 安装 mysql8.0

bash 复制代码
docker run --name mysql -e MYSQL_ROOT_PASSWORD=123456 -v /root/mysqlData:/var/lib/mysql --privileged=true -d -p 3306:3306 --network canal_net mysql
 
 
//--name test 为容器指定一个名称(可替换test为自己喜欢的名称)。
//-e MYSQL_ROOT_PASSWORD=1 设置MySQL的root密码。你应该替换1为你的实际密码。
//-d 使容器在后台运行。
//mysql:latest 是要运行的MySQL镜像和标签(在这里是最新版本) 
//以数据卷的形式,创建并启动mysql容器,容器内的mysql数据不会因为容器的删除而被删除
//-v /root/mysqlData:/var/lib/mysql:将数据卷/root/mysqlData映射到容器的/var/lib/mysql目录
//-p 3306:3306:将容器的3306端口映射到主机的3306端口
//--privileged=true: 使容器内的root拥有真正的root权限,解决挂载目录没有权限的问题

此处只简单列了docker安装mysql 的命令。

2.3 检查配置mysql 是否开启了binlog日志

sql 复制代码
-- 查看是否开启 binlog
show variables like "%log_bin%";
-- 选择 ROW 模式
show variables like "binlog_format";
-- 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
show variables like "server_id";

mysql8.0默认开启 binlog 日志 ,无需修改配置。

开启binlog日志方法:

打开mysql挂载目录下( /mydata/mysql/conf )的 my.cnf 配置文件,添加以下配置

bash 复制代码
[mysqld]
log-bin=mysql-bin # 开启 binlog
binlog-format=ROW # 选择 ROW 模式
server_id=1 # 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复

2.4 在 mysql 中新建canal专用账户并设置权限

sql 复制代码
create user canal@'%' IDENTIFIED by 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%' identified by 'canal';
FLUSH PRIVILEGES;

三、docker 安装 canal-admin

3.1 拉取 canal-admin 镜像

bash 复制代码
docker pull canal/canal-admin

3.2 创建 canal_manager 数据库

在官网 https://github.com/alibaba/canal/releases的canal.admin-1.1.8-SNAPSHOT.tar.gz文件的解压包中找到canal_manager.sql,并在mysql中执行脚本。

mysql 脚本(1.1.8 版本)如下:

sql 复制代码
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `canal_manager` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */;

USE `canal_manager`;

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for canal_adapter_config
-- ----------------------------
DROP TABLE IF EXISTS `canal_adapter_config`;
CREATE TABLE `canal_adapter_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `category` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `status` varchar(45) DEFAULT NULL,
  `content` text NOT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_cluster
-- ----------------------------
DROP TABLE IF EXISTS `canal_cluster`;
CREATE TABLE `canal_cluster` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `zk_hosts` varchar(255) NOT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_config
-- ----------------------------
DROP TABLE IF EXISTS `canal_config`;
CREATE TABLE `canal_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `cluster_id` bigint(20) DEFAULT NULL,
  `server_id` bigint(20) DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  `status` varchar(45) DEFAULT NULL,
  `content` text NOT NULL,
  `content_md5` varchar(128) NOT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `sid_UNIQUE` (`server_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_instance_config
-- ----------------------------
DROP TABLE IF EXISTS `canal_instance_config`;
CREATE TABLE `canal_instance_config` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `cluster_id` bigint(20) DEFAULT NULL,
  `server_id` bigint(20) DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  `status` varchar(45) DEFAULT NULL,
  `content` text NOT NULL,
  `content_md5` varchar(128) DEFAULT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name_UNIQUE` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_node_server
-- ----------------------------
DROP TABLE IF EXISTS `canal_node_server`;
CREATE TABLE `canal_node_server` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `cluster_id` bigint(20) DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  `ip` varchar(255) NOT NULL,
  `admin_port` int(11) DEFAULT NULL,
  `tcp_port` int(11) DEFAULT NULL,
  `metric_port` int(11) DEFAULT NULL,
  `status` varchar(45) DEFAULT NULL,
  `modified_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for canal_user
-- ----------------------------
DROP TABLE IF EXISTS `canal_user`;
CREATE TABLE `canal_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `roles` varchar(255) NOT NULL,
  `introduction` varchar(255) DEFAULT NULL,
  `avatar` varchar(255) DEFAULT NULL,
  `creation_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

-- ----------------------------
-- Records of canal_user
-- ----------------------------
BEGIN;
INSERT INTO `canal_user` VALUES (1, 'admin', '6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9', 'Canal Manager', 'admin', NULL, NULL, '2019-07-14 00:05:28');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

3.3 配置application.yml

在宿主机新建目录/mydata/canal/admin/conf,并新建application.yml文件

bash 复制代码
// application.yml 文件的内容

server:
  port: 8089
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

spring.datasource:
  address: mysql:3306
  database: canal_manager
  username: root
  password: root
  driver-class-name: com.mysql.jdbc.Driver
  url: jdbc:mysql://${spring.datasource.address}/${spring.datasource.database}?useUnicode=true&characterEncoding=UTF-8&useSSL=false
  hikari:
    maximum-pool-size: 30
    minimum-idle: 1

canal:
  adminUser: admin
  adminPasswd: admin

3.4 docker 运行 canal-admin

bash 复制代码
// 以参数形式运行,无需在application.yml文件中填写参数
docker run -d --name canal-admin  -e spring.datasource.address=mysql:3306 -e spring.datasource.database=canal_manager -e spring.datasource.username=root -e spring.datasource.password=foodbook --network canal_net -p 8089:8089 canal/canal-admin

// 以挂载文件方式运行,数据库的配置在application.yml文件中填好
docker run -d --name canal-admin  \
-v /mydata/canal/admin/conf/application.yml:/home/admin/canal-admin/conf/application.yml \
-v /mydata/canal/admin/logs/:/home/admin/canal-admin/logs/ \
--network canal_net -p 8089:8089 canal/canal-admin

启动成功后,可在挂载的日志目录下查看 canal-admin 的启动日志:

此时代表canal-admin已经启动成功。

备注:我在阿里云服务器上,用docker安装 canal-admin 后,启动失败,查询日志,发现运行该服务需要2g内存,我服务器内存不够。

3.5 访问 canal-admin 后台管理页面

可以通过 http://127.0.0.1:8089/ 访问,默认密码:admin/123456

四、docker 安装 canal 服务

官网安装方法:https://github.com/alibaba/canal/wiki/Docker-QuickStart

4.1 拉取 canal 镜像

bash 复制代码
docker pull canal/canal-server

4.2 运行 canal

bash 复制代码
docker run --name canal-server -d canal/canal-server

4.3 复制canal容器内的配置文件到宿主机

bash 复制代码
# 创建文件夹并复制文件
mkdir /mydata/canal
chmod 777  /mydata/canal
docker cp canal-server:/home/admin/canal-server/conf/canal.properties /mydata/canal/conf
docker cp canal-server:/home/admin/canal-server/conf/example/instance.properties /mydata/canal/conf/example

4.4 修改 canal.properties 配置文件

canal.properties 文件配置 canal-server 服务的参数,主要修改内容:

bash 复制代码
# register ip to zookeeper # canal-server 服务自身的IP,因为运行 docker 时使用了 dockers network 网络,所以这里直接配置了 canal-server 的服务名
canal.register.ip = canal-server  
canal.port = 11111
canal.metrics.pull.port = 11112
# canal instance user/passwd
# canal.user = canal
# canal.passwd =

# canal admin config 这里配置 canal-admin 的IP和端口,以便 canal-admin 管理页面能显示和配置canal 服务,注意用户名和密码来自于 canal-admin 服务配置的 aplication.yml 中的adminUser、adminPassword,默认都为 admin(4ACFE3202A5FF5CF467898FC58AAB1D615029441是admin加密字符串)
canal.admin.manager = canal-admin: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 = ddddddd  # canal-server 的服务名

具体配置项如下:

bash 复制代码
#################################################
######### 		common argument		#############
#################################################
# tcp bind ip
canal.ip = 
# register ip to zookeeper # canal-server 服务自身的IP,因为运行 docker 时使用了 dockers network 网络,所以这里直接配置了 canal-server 的服务名
canal.register.ip = canal-server  
canal.port = 11111
canal.metrics.pull.port = 11112
# canal instance user/passwd
# canal.user = canal
# canal.passwd =

# canal admin config 这里配置 canal-admin 的IP和端口,以便 canal-admin 管理页面能显示和配置canal 服务,注意用户名和密码来自于 canal-admin 服务配置的 aplication.yml 中的adminUser、adminPassword,默认都为 admin(4ACFE3202A5FF5CF467898FC58AAB1D615029441是admin加密字符串)
canal.admin.manager = canal-admin: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 = ddddddd  # canal-server 的服务名

canal.zkServers =
# flush data to zk
canal.zookeeper.flush.period = 1000
canal.withoutNetty = false
# tcp, kafka, rocketMQ, rabbitMQ, pulsarMQ
canal.serverMode = tcp
# flush meta cursor/parse position to file
canal.file.data.dir = ${canal.conf.dir}
canal.file.flush.period = 1000
## memory store RingBuffer size, should be Math.pow(2,n)
canal.instance.memory.buffer.size = 16384
## memory store RingBuffer used memory unit size , default 1kb
canal.instance.memory.buffer.memunit = 1024 
## meory store gets mode used MEMSIZE or ITEMSIZE
canal.instance.memory.batch.mode = MEMSIZE
canal.instance.memory.rawEntry = true

## detecing config
canal.instance.detecting.enable = false
#canal.instance.detecting.sql = insert into retl.xdual values(1,now()) on duplicate key update x=now()
canal.instance.detecting.sql = select 1
canal.instance.detecting.interval.time = 3
canal.instance.detecting.retry.threshold = 3
canal.instance.detecting.heartbeatHaEnable = false

# support maximum transaction size, more than the size of the transaction will be cut into multiple transactions delivery
canal.instance.transaction.size =  1024
# mysql fallback connected to new master should fallback times
canal.instance.fallbackIntervalInSeconds = 60

# network config
canal.instance.network.receiveBufferSize = 16384
canal.instance.network.sendBufferSize = 16384
canal.instance.network.soTimeout = 30

# binlog filter config
canal.instance.filter.druid.ddl = true
canal.instance.filter.query.dcl = false
canal.instance.filter.query.dml = false
canal.instance.filter.query.ddl = false
canal.instance.filter.table.error = false
canal.instance.filter.rows = false
canal.instance.filter.transaction.entry = false
canal.instance.filter.dml.insert = false
canal.instance.filter.dml.update = false
canal.instance.filter.dml.delete = false

# binlog format/image check
canal.instance.binlog.format = ROW,STATEMENT,MIXED 
canal.instance.binlog.image = FULL,MINIMAL,NOBLOB

# binlog ddl isolation
canal.instance.get.ddl.isolation = false

# parallel parser config
canal.instance.parser.parallel = true
## concurrent thread number, default 60% available processors, suggest not to exceed Runtime.getRuntime().availableProcessors()
#canal.instance.parser.parallelThreadSize = 16
## disruptor ringbuffer size, must be power of 2
canal.instance.parser.parallelBufferSize = 256

# table meta tsdb info
canal.instance.tsdb.enable = true
canal.instance.tsdb.dir = ${canal.file.data.dir:../conf}/${canal.instance.destination:}
canal.instance.tsdb.url = jdbc:h2:${canal.instance.tsdb.dir}/h2;CACHE_SIZE=1000;MODE=MYSQL;
canal.instance.tsdb.dbUsername = canal
canal.instance.tsdb.dbPassword = canal
# dump snapshot interval, default 24 hour
canal.instance.tsdb.snapshot.interval = 24
# purge snapshot expire , default 360 hour(15 days)
canal.instance.tsdb.snapshot.expire = 360

#################################################
######### 		destinations		#############
#################################################
canal.destinations = example
# conf root dir
canal.conf.dir = ../conf
# auto scan instance dir add/remove and start/stop instance
canal.auto.scan = true
canal.auto.scan.interval = 5
# set this value to 'true' means that when binlog pos not found, skip to latest.
# WARN: pls keep 'false' in production env, or if you know what you want.
canal.auto.reset.latest.pos.mode = false

canal.instance.tsdb.spring.xml = classpath:spring/tsdb/h2-tsdb.xml
#canal.instance.tsdb.spring.xml = classpath:spring/tsdb/mysql-tsdb.xml

canal.instance.global.mode = spring
canal.instance.global.lazy = false
canal.instance.global.manager.address = ${canal.admin.manager}
#canal.instance.global.spring.xml = classpath:spring/memory-instance.xml
canal.instance.global.spring.xml = classpath:spring/file-instance.xml
#canal.instance.global.spring.xml = classpath:spring/default-instance.xml

##################################################
######### 	      MQ Properties      #############
##################################################
# aliyun ak/sk , support rds/mq
canal.aliyun.accessKey =
canal.aliyun.secretKey =
canal.aliyun.uid=

canal.mq.flatMessage = true
canal.mq.canalBatchSize = 50
canal.mq.canalGetTimeout = 100
# Set this value to "cloud", if you want open message trace feature in aliyun.
canal.mq.accessChannel = local

canal.mq.database.hash = true
canal.mq.send.thread.size = 30
canal.mq.build.thread.size = 8

##################################################
######### 		     Kafka 		     #############
##################################################
kafka.bootstrap.servers = 127.0.0.1:9092
kafka.acks = all
kafka.compression.type = none
kafka.batch.size = 16384
kafka.linger.ms = 1
kafka.max.request.size = 1048576
kafka.buffer.memory = 33554432
kafka.max.in.flight.requests.per.connection = 1
kafka.retries = 0

kafka.kerberos.enable = false
kafka.kerberos.krb5.file = ../conf/kerberos/krb5.conf
kafka.kerberos.jaas.file = ../conf/kerberos/jaas.conf

# sasl demo
# kafka.sasl.jaas.config = org.apache.kafka.common.security.scram.ScramLoginModule required \\n username=\"alice\" \\npassword="alice-secret\";
# kafka.sasl.mechanism = SCRAM-SHA-512
# kafka.security.protocol = SASL_PLAINTEXT

##################################################
######### 		    RocketMQ	     #############
##################################################
rocketmq.producer.group = test
rocketmq.enable.message.trace = false
rocketmq.customized.trace.topic =
rocketmq.namespace =
rocketmq.namesrv.addr = 127.0.0.1:9876
rocketmq.retry.times.when.send.failed = 0
rocketmq.vip.channel.enabled = false
rocketmq.tag = 

##################################################
######### 		    RabbitMQ	     #############
##################################################
rabbitmq.host =
rabbitmq.virtual.host =
rabbitmq.exchange =
rabbitmq.username =
rabbitmq.password =
rabbitmq.queue =
rabbitmq.routingKey =
rabbitmq.deliveryMode =


##################################################
######### 		      Pulsar         #############
##################################################
pulsarmq.serverUrl =
pulsarmq.roleToken =
pulsarmq.topicTenantPrefix =

4.5 修改 example 文件夹下的 instance.properties 配置参数

bash 复制代码
server:
  port: 8089
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

spring.datasource:
  address: mysql:3306
  database: canal_manager
  username: root
  password: 123456
  driver-class-name: com.mysql.jdbc.Driver
  url: jdbc:mysql://${spring.datasource.address}/${spring.datasource.database}?useUnicode=true&characterEncoding=UTF-8&useSSL=false
  hikari:
    maximum-pool-size: 30
    minimum-idle: 1

canal:
  adminUser: admin
  adminPasswd: admin

4.6 删除canal容器

bash 复制代码
docker rm -f canal-server

4.7 以挂载文件的方式运行 canal 容器

bash 复制代码
// 重要:注意挂载日志文件到宿主机,方便查看容器运行日志
docker run --name canal-server -p 11111:11111 -d \
-v /mydata/canal/conf/example/instance.properties:/home/admin/canal-server/conf/example/instance.properties \
-v /mydata/canal/conf/canal.properties:/home/admin/canal-server/conf/canal.properties \
-v /mydata/canal/logs/:/home/admin/canal-server/logs/  \
--network canal_net \
canal/canal-server

备注:canal 容器中日志文件路径为:/home/admin/canal-server/logs/

注意:如果在宿主机上配置好了 instance.properties 文件内容,就无需进行4.2到4.6步骤,直接执行4.7步骤即可。

4.8 canal-admin 后台页面中管理服务及实例

可以在 【instance 管理】中新增或修改 instance 实例,在此新增的实例会将配置文件信息保存到 canal-manager 数据库的表 canal_instance_config 中。

在 canal-manager 数据库中查看新配置的实例,如下:

五、基于 go 语言订阅消费 canal-server

参考 github 库:https://github.com/withlin/canal-go

5.1 go 代码

Go 复制代码
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/withlin/canal-go/client"
	pbe "github.com/withlin/canal-go/protocol/entry"
	"google.golang.org/protobuf/proto"
)

func main() {

	// 192.168.199.17 替换成你的canal server的地址
	// example 替换成-e canal.destinations=example 你自己定义的名字
	connector := client.NewSimpleCanalConnector("172.28.203.173", 11111, "", "", "example", 60000, 60*60*1000)
	err := connector.Connect()
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	// https://github.com/alibaba/canal/wiki/AdminGuide
	//mysql 数据解析关注的表,Perl正则表达式.
	//
	//多个正则之间以逗号(,)分隔,转义符需要双斜杠(\\)
	//
	//常见例子:
	//
	//  1.  所有表:.*   or  .*\\..*
	//	2.  canal schema下所有表: canal\\..*
	//	3.  canal下的以canal打头的表:canal\\.canal.*
	//	4.  canal schema下的一张表:canal\\.test1
	//  5.  多个规则组合使用:canal\\..*,mysql.test1,mysql.test2 (逗号分隔)

	err = connector.Subscribe(".*\\..*")
	if err != nil {
		log.Println(err)
		os.Exit(1)
	}

	for {

		message, err := connector.Get(100, nil, nil)
		if err != nil {
			log.Println(err)
			os.Exit(1)
		}
		batchId := message.Id
		if batchId == -1 || len(message.Entries) <= 0 {
			time.Sleep(300 * time.Millisecond)
			fmt.Println("===没有数据了===")
			continue
		}

		printEntry(message.Entries)

	}
}

func printEntry(entrys []pbe.Entry) {

	for _, entry := range entrys {
		if entry.GetEntryType() == pbe.EntryType_TRANSACTIONBEGIN || entry.GetEntryType() == pbe.EntryType_TRANSACTIONEND {
			continue
		}
		rowChange := new(pbe.RowChange)

		err := proto.Unmarshal(entry.GetStoreValue(), rowChange)
		checkError(err)
		if rowChange != nil {
			eventType := rowChange.GetEventType()
			header := entry.GetHeader()
			fmt.Println(fmt.Sprintf("================> binlog[%s : %d],name[%s,%s], eventType: %s", header.GetLogfileName(), header.GetLogfileOffset(), header.GetSchemaName(), header.GetTableName(), header.GetEventType()))

			for _, rowData := range rowChange.GetRowDatas() {
				if eventType == pbe.EventType_DELETE {
					printColumn(rowData.GetBeforeColumns())
				} else if eventType == pbe.EventType_INSERT {
					printColumn(rowData.GetAfterColumns())
				} else {
					fmt.Println("-------> before")
					printColumn(rowData.GetBeforeColumns())
					fmt.Println("-------> after")
					printColumn(rowData.GetAfterColumns())
				}
			}
		}
	}
}

func printColumn(columns []*pbe.Column) {
	for _, col := range columns {
		fmt.Println(fmt.Sprintf("%s : %s  update= %t", col.GetName(), col.GetValue(), col.GetUpdated()))
	}
}

func checkError(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error())
		os.Exit(1)
	}
}

5.2 效果演示

更新数据库表中的数据,可以看到 go 程序打印出 mysql 数据更新前后的变化:

六、报错问题

6.1 canal 服务运行后闪退

如何查看详细错误日志?

docker 运行 canal 服务时,将容器的错误日志(canal容器日志文件默认路径:/home/admin/canal-server/logs/)挂载到宿主机上, 即可查看详细日志:

发现是宿主机运行内存不够,用 docker status 查看,果然剩余内存不够运行 canal 服务,停止暂时不用的容器,再启动 canal-server,成功运行。

相关推荐
椒盐螺丝钉2 小时前
TypeScript类型兼容性
运维·前端·typescript
老黄编程2 小时前
ubuntu如何查看一个内核模块被什么模块依赖(内核模块信息常用命令)?
linux·运维·ubuntu
Freed&3 小时前
Ansible 生产级自动化指南:Playbook、Handlers、Jinja2 全解析
运维·自动化·ansible
b***25113 小时前
储能电池包的自动化产线探秘|深圳比斯特自动化
运维·自动化
ZeroNews内网穿透3 小时前
新版发布!“零讯”微信小程序版本更新
运维·服务器·网络·python·安全·微信小程序·小程序
工控小楠3 小时前
涡街流量计温度数据的协议桥梁:Modbus RTU 转 Profinet 网关的自动化应用
运维·自动化
<但凡.3 小时前
Linux 修炼:进程控制(一)
linux·运维·服务器·bash
杨浦老苏3 小时前
文件共享应用程序Palmr
docker·群晖·网盘
m0_464608264 小时前
Ansible实现自动化运维
运维·自动化·ansible