软件版本
CentOS Linux release 7.6.1810 (Core)
Docker version 20.10.3
canal-admin v1.1.5
canal-server v1.1.5
mysql 5.7
mysql 8
wurstmeister/kafka 2.13-2.7.0
zookeeper 3.6.3
docker常用命令
-
下载镜像(可在dockerHub查找镜像:dockerHub官网地址)
docker pull mysql
-
删除镜像(当没有使用此镜像的容器存在时,才能删除)
docker rmi mysql
-
运行容器(还有很多参数可以选择,详情查看docker run --help)
docker run -d // 后台运行 --name mysql5.7 //指定名称 -p 3305:3306 //端口映射 -v /home/docker/mysql/5.7/data/mysql:/var/lib/mysql //容器卷映射 -e MYSQL_ROOT_PASSWORD=123456 //参数传递 mysql //指定镜像
-
查询容器(加上-a可以查询所有容器,包括失败容器)
docker ps
-
重启容器(当容器关闭后才能重启)
docker restart mysql
-
关闭容器
docker stop mysql
-
删除容器
docker rm mysql
-
进入容器
docker exec -it mysql bash
Mysql配置
mysql5.7
1)、docker容器运行(此步骤只是先将容器配置以及数据复制到宿主机)
docker run -d --name mysql5.7 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
2)、拷贝容器配置及数据
\\ 需先建立目录
mkdir /home/docker/mysql/5.7/data
mkdir /home/docker/mysql/5.7/conf
\\ 拷贝数据(这是mysql的表数据以及日志数据)
docker cp mysql5.7:/var/lib/mysql /home/docker/mysql/5.7/data
\\ 拷贝配置文件
docker cp mysql5.7:/etc/mysql /home/docker/mysql/5.7/conf
3)、删除容器
docker stop mysql5.7
docker rm mysql5.7
4)、运行加了容器卷映射的容器(注意这里容器卷的映射和上面拷贝的地址是不一样的,多加了一层目录)。这里制定了root账户的密码是123456
docker run \
-d \
--name mysql5.7 \
-p 3305:3306 \
-v /home/docker/mysql/5.7/data/mysql:/var/lib/mysql \
-v /home/docker/mysql/5.7/conf/mysql:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:5.7
5)、修改配置,开启binlog,以row形式进行增量记录
修改 /home/docker/mysql/5.7/conf/mysql下的my.cnf、mysql.cnf、mysql.conf.d下的或者conf.d下的任一文件。server-id需在mysql中保持唯一
log-bin=/var/lib/mysql/mysql-bin
server-id=123454
binlog-format=ROW
6)、重启容器
docker restart mysql5.7
7)、连接mysql,在mysql中执行下列脚本。查看容器是否开启binlog(为NO则为开启,OFF为没开启)
SHOW GLOBAL VARIABLES LIKE '%log_bin%';
但是 !!! 我遇到了第一个坑,mysql5.7是默认不开启binlog日志的,我修改宿主机配置文件后重启是没有开启binlog(安装上述方式修改配置文件)。最后找到了另外的方式去修改。
通过shell命令去修改容器中的文件,再重启就成功开启binlog日志。这也是一种修改docker容器中文件的方式。
docker exec mysql5.7 bash -c "echo 'log-bin=/var/lib/mysql/mysql-bin' >> /etc/mysql/mysql.conf.d/mysqld.cnf"
docker exec mysql5.7 bash -c "echo 'server-id=123454' >> /etc/mysql/mysql.conf.d/mysqld.cnf"
mysql8.0
mysql8是默认开启binlog的,可以按照上述方式启动mysql8,不用修改配置。
kafka
先启动zookeeper
docker run -d --name zookeeper -p 2181:2181 zookeeper:3.6.3
启动kafka
docker run -d --name kafka \
-p 9092:9092 \
-e KAFKA_BROKER_ID=0 \
-e KAFKA_ZOOKEEPER_CONNECT=zookeeper的ip:2181 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://当前主机的ip:9092 \
-e KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 wurstmeister/kafka:2.13-2.7.0
可以使用Offset Explorer连接kafka查看数据:Offset Explorer下载地址
canal-admin
canal-admin是canal-server的界面管理。可要可不要,如果没有canal-admin,canal-server也能使用,稍后会讲解两种方式的canal-server的配置方式。
1、启动canal-admin
1.1 使用官方shell启动脚本
下载shell脚本(下载失败就多试几次,可能是网络原因)
wget https://raw.githubusercontent.com/alibaba/canal/master/docker/run_admin.sh
1)、使用canal-admin内置的数据库时候的启动方式(密码需要为6位以上,不然登录不了)
sh run_admin.sh \
-e server.port=8089 \
-e canal.adminUser=admin \
-e canal.adminPasswd=123456
2)、使用外部的数据库时候的启动方式
sh run_admin.sh \
-e server.port=8089 \
-e canal.adminUser=admin \
-e canal.adminPasswd=123456 \
-e spring.datasource.address= \
-e spring.datasource.database=canal_manager \
-e spring.datasource.username= \
-e spring.datasource.password=
外部数据库的脚本: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(45) NOT NULL,
`name` varchar(45) 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(63) 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(45) 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(45) 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(63) NOT NULL,
`ip` varchar(63) 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(31) NOT NULL,
`password` varchar(128) NOT NULL,
`name` varchar(31) NOT NULL,
`roles` varchar(31) 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;
使用shell脚本启动的容器是不显示的端口映射的,因为启动是指定了–net=host,表示直接引用宿主机端口。
1.2 使用docker命令启动
docker run -it --name canal-admin \
-p 8089:8089 \
-e server.port=8089 \
-e canal.adminUser=admin \
-e canal.adminPasswd=123456 \
-e spring.datasource.address= \
-e spring.datasource.database=canal_manager \
-e spring.datasource.username= \
-e spring.datasource.password= \
-d canal/canal-admin:v1.1.5
启动成功后浏览器访问 ip:8089 查看canal-admin的页面。如果访问失败,则再看canal-admin是否挂掉了。
2 启动失败
如果canal-admin启动失败。
1、内存不足。当内存不足时,最后启动的容器会挂掉
2、数据库连接失败。可以去查看canal-admin的日志。
这里的查看canal-admin的日志不是docker logs去查看启动日志。而是进入容器里面查看日志。就是拼手速,只要手速快,就能在容器挂掉前看到是什么错误。也可以在启动容器时候将日志映射到宿主机。
canal-admin的日志地址。
/home/admin/canal-admin/logs/admin.log
如果出现如下错误:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Public Key Retrieval is not allowed
莫慌,让子弹飞一会。这个错误找了网上好久,但都说是在url上加 allowPublicKeyRetrieval=true。但我在没加这个参数上,多启动了几次mysql,也是连接上了。而且我也在本地项目中使用canal中的数据库连接是没有报上面的错误。所以这个错误我有点困惑。希望知道的小伙伴能再评论区中留下解答
canal-server
canal-server是真正工作的。是将自己伪装成mysql的从库去获取mysql的binlog并解析,可以自己选择是tcp, kafka, rocketMQ, rabbitMQ这几种中选择一种方式推送解析后的日志数据。我这里选择的是kafka,因为kafka的吞吐量是在mq中是最好的。
1 使用官方shell启动脚本(连接canal-admin)
1.1、启动canal-server
1)、下载shell脚本(下载失败就多试几次,可能是网络原因)。
wget https://raw.githubusercontent.com/alibaba/canal/master/docker/run.sh
密码为canal_manager数据库中canal_user中的passwprd。如果未曾更改那就是下面的密码。
sudo sh run.sh
-e canal.admin.manager=canal-admin的ip:8089 \
-e canal.admin.port=11110 \
-e canal.admin.user=admin \
-e canal.admin.passwd=6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9
2)、如果连接成功,则再canal-admin可以看到有个server是启动状态的。
3)、如果不是启动状态的,就再启动一次,拼手气进入容器去看日志。大概率是密码问题。
日志地址
/home/admin/canal-server/logs/canal/canal.log
1.2、修改配置
1)修改server配置。这个配置的是canal的通用配置,指定数据输出口和一些通用过滤配置。
点击操作-》配置-》载入模板可以看到如下界面
修改如下配置
# tcp, kafka, rocketMQ, rabbitMQ
canal.serverMode = kafka
kafka.bootstrap.servers = kafka的ip:9092
修改完之后点击保存,再返回,如果返回之后是不是启动状态,那就是配置改的有问题,需要修改。可以再次拼手速去容器里面查看日志。也可以在界面上操作-》日志查看日志。
2)、修改instance配置。这个配置的是数据库以及mq的(主题、分区)。这里是一个instance监听一个mysql。
3)、点击操作-》修改。也可以是新建instance-》载入模板
修改如下配置
canal.instance.master.address=数据库的ip:3306
canal.instance.dbUsername=数据库连接名
canal.instance.dbPassword=数据库连接密码
点击保存或修改就可以,返回后如果不是启动状态,则在操作-》日志去看为什么启动失败,大概率是因为数据库的密码问题
Caused by: java.io.IOException: caching_sha2_password Auth failed
出现这个异常,是因为mysql8的认证规则有做修改。需要修改账户的认证规则。
#更新一下用户密码,这里是把账户为canal的密码修改为使用mysql_native_password规则生成,密码为canal。
ALTER USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal';
#刷新权限
FLUSH PRIVILEGES;
1 使用docker命令启动(不连接canal-admin)
1.1、启动canal-server
docker run \
-d \
-e canal.admin.manager=192.168.174.132:8089 \
-e canal.admin.port=11110 \
-e canal.admin.user=admin \
-e canal.admin.passwd=4acfe3202a5ff5cf467898fc58aab1d615029441 \
--name canal-server \
-p 11111:11111 \
canal/canal-server:v1.1.5
1.2、修改配置
server配置文件,修改上述server配置文件一样
/home/admin/canal-server/conf/canal.properties
instance配置文件,修改上述instance配置文件一样
/home/admin/canal-server/conf/example/instance.properties
获取kakfa数据
1、添加依赖
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>2.7.1</version>
</dependency>
2、添加配置
spring:
kafka:
consumer:
# 是否自动提交offset
enable-auto-commit: true
# 默认消费组
group-id: consumer
# 当kafka中没有初始offset或offset超出范围时将自动重置offset
# earliest:重置为分区中最小的offset;
# latest:重置为分区中最新的offset(消费分区中新产生的数据);
# none:只要有一个分区不存在已提交的offset,就抛出异常;
auto-offset-reset: earliest
bootstrap-servers: 192.168.174.132:9092
3、添加消费代码
@Slf4j
@Component
public class kafkaConsumer {
@KafkaListener(topics = {"example"})
public void onMessage3(ConsumerRecord<?, ?> record) {
log.info("==========================start===================" +
"\r\ntopic:"+record.topic()+"\r\npartition:"+record.partition()+"\r\noffset:"+record.offset()+"\r\nvalue:"+record.value()+"\r\n" +
"==========================end===================");
}
@KafkaListener(id ="example5.7", topicPartitions = {
@TopicPartition(topic = "example5.7", partitionOffsets = @PartitionOffset(partition = "0", initialOffset = "1"))
})
public void example5(ConsumerRecord<?, ?> record) {
log.info("\r\n==========================start5.7===================" +
"\r\ntopic:"+record.topic()+"\r\npartition:"+record.partition()+"\r\noffset:"+record.offset()+"\r\nvalue:"+record.value()+"\r\n" +
"==========================end===================");
}
}
4、查看消费日志
// 新增语句,新增name为‘测试’,version为‘1’的数据
==========================start5.7===================
topic:example5.7
partition:0
offset:16
value:{"data":[{"id":"5","name":"测试","version":"1"}],"database":"user","es":1625556602000,"id":2,"isDdl":false,"mysqlType":{"id":"int","name":"varchar(255)","version":"varchar(255)"},"old":null,"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12,"version":12},"table":"person","ts":1625556603117,"type":"INSERT"}
==========================end===================
// 修改语句,修改version为‘2’
==========================start5.7===================
topic:example5.7
partition:0
offset:17
value:{"data":[{"id":"5","name":"测试","version":"2"}],"database":"user","es":1625556645000,"id":3,"isDdl":false,"mysqlType":{"id":"int","name":"varchar(255)","version":"varchar(255)"},"old":[{"version":"1"}],"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12,"version":12},"table":"person","ts":1625556645808,"type":"UPDATE"}
==========================end===================
// 删除语句
==========================start5.7===================
topic:example5.7
partition:0
offset:18
value:{"data":[{"id":"5","name":"测试","version":"2"}],"database":"user","es":1625556925000,"id":4,"isDdl":false,"mysqlType":{"id":"int","name":"varchar(255)","version":"varchar(255)"},"old":null,"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12,"version":12},"table":"person","ts":1625556925623,"type":"DELETE"}
==========================end===================
// person表添加password字段
==========================start5.7===================
topic:example5.7
partition:0
offset:19
value:{"data":null,"database":"user","es":1625557083000,"id":5,"isDdl":true,"mysqlType":null,"old":null,"pkNames":null,"sql":"ALTER TABLE `user`.`person` \r\nADD COLUMN `password` varchar(255) NULL AFTER `version`","sqlType":null,"table":"person","ts":1625557083503,"type":"ALTER"}
==========================end===================
可以看到isDdl字段,是判断是不是ddl语句,如果是ddl语句,sql字段就有sql语句,不是ddl语句就没有。
DDL(Data Definition Language)数据定义语言:对数据库中的某些对象(例如,database,table)进行管理,如Create,Alter和Drop.
DML(Data Manipulation Language)数据操纵语言:对数据库中的数据进行一些简单操作,如insert,delete,update,select等.
如果DML语句也要有sql语句怎么弄呢?
可以查看官方给出的解释:官方回应dml语句获取sql
本人经过测试是可以的。只是数据需要过滤一些。
==========================start5.7===================
topic:example5.7
partition:0
offset:8
value:{"data":null,"database":"","es":1625215656000,"id":1,"isDdl":false,"mysqlType":null,"old":null,"pkNames":null,"sql":"INSERT INTO `user`.`person`(`name`) VALUES ('神神道道')","sqlType":null,"table":"person","ts":1625215668538,"type":"QUERY"}
==========================end===================
==========================start5.7===================
topic:example5.7
partition:0
offset:9
value:{"data":[{"id":"3","name":"神神道道"}],"database":"user","es":1625215656000,"id":1,"isDdl":false,"mysqlType":{"id":"int","name":"varchar(255)"},"old":null,"pkNames":["id"],"sql":"","sqlType":{"id":4,"name":12},"table":"person","ts":1625215668538,"type":"INSERT"}
==========================end===================
这是一条新增语句,新增一条name为’神神道道’的数据。可以看到这里是打印了两条。一条type为"INSERT",一条为"QUERY"。一条有sql语句,一条没有。所以入库前需要对数据做数据过滤。这个涉及到数据清洗的步骤,之后会出这方面的文章。