前言
最近业务需要监听mysql的binlog日志,于是就看了下canal,下面记录踩到的坑。
简介
canal [kə’næl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费
早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。
基于日志增量订阅和消费的业务包括
- 数据库镜像
- 数据库实时备份
- 索引构建和实时维护(拆分异构索引、倒排索引等)
- 业务 cache 刷新
- 带业务逻辑的增量数据处理
Canal 的工作原理
- MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
- MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
- MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
canal 工作原理
- canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送dump 协议
- MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 canal )
- canal 解析 binary log 对象(原始为 byte 流)
搭建环境
中间件 | 版本 |
---|---|
mysql | 5.7 |
kafka | 2.13-2.4.0 |
zookeeper | 3.6.0 |
canal | 1.1.4 |
安装mysql
这个我用的docker-compose
安装,需要安装的可以参考我之前的博客
docker搭建mysql主从搭建及Sharding-Jdbc读写分离,这篇博客我详细讲解了如何搭建mysql主从。但是要注意的是,binlog format
需要设置为row
模式。
├── docker-compose.yml
├── master
│ ├── Dockerfile
│ └── my.cnf
├── slave
│ ├── Dockerfile
│ └── my.cnf
└── slave2
├── Dockerfile
└── my.cnf
这里需要改下my.cnf
文件
binlog_format=mixed
启动成功,输入SHOW VARIABLES LIKE '%bin%';
对照下下面两行。
安装Zookeeper及Kafka
Canal和Kafka集群都依赖于Zookeeper做服务协调,为了方便管理,一般会独立部署Zookeeper服务或者Zookeeper集群。这里使用docker-compose脚本部署,脚本如下:
version: '2'
services:
zookeeper:
image: wurstmeister/zookeeper ## 镜像
ports:
- "2181:2181" ## 对外暴露的端口号
kafka:
image: wurstmeister/kafka ## 镜像
volumes:
- /etc/localtime:/etc/localtime ## 挂载位置(kafka镜像和宿主机器之间时间保持一直)
ports:
- "9092:9092"
environment:
KAFKA_ADVERTISED_HOST_NAME: 192.168.56.119 ## 修改:宿主机IP
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 ## 卡夫卡运行是基于zookeeper的
kafka-manager:
image: sheepkiller/kafka-manager ## 镜像:开源的web管理kafka集群的界面
environment:
ZK_HOSTS: ## 修改:宿主机IP
ports:
- "9000:9000"
docker-compose up -d
Starting kafka_kafka_1 ... done
Starting kafka_zookeeper_1 ... done
Starting kafka_kafka-manager_1 ... done
搭建canal
终于到了主角登场,这里选用Canal的v1.1.4稳定发布版,只需要下载deployer模块:
mkdir /data/canal
cd /data/canal
# 这里注意一点,Github在国内被墙,下载速度极慢,可以先用其他下载工具下载完再上传到服务器中
wget https://github.com/alibaba/canal/releases/download/canal-1.1.4/canal.deployer-1.1.4.tar.gz
tar -zxvf canal.deployer-1.1.4.tar.gz
解压后的目录如下:
- bin # 运维脚本
- conf # 配置文件
canal_local.properties # canal本地配置,一般不需要动
canal.properties # canal服务配置
logback.xml # logback日志配置
metrics # 度量统计配置
spring # spring-实例配置,主要和binlog位置计算、一些策略配置相关,可以在canal.properties选用其中的任意一个配置文件
example # 实例配置文件夹,一般认为单个数据库对应一个独立的实例配置文件夹
instance.properties # 实例配置,一般指单个数据库的配置
- lib # 服务依赖包
- logs # 日志文件输出目录
在开发和测试环境建议把logback.xml的日志级别修改为DEBUG方便定位问题。这里需要关注canal.properties和instance.properties两个配置文件。canal.properties文件中,需要修改:
- 去掉canal.instance.parser.parallelThreadSize = 16这个配置项的注释,也就是启用此配置项,和实例解析器的线程数相关,不配置会表现为阻塞或者不进行解析。
- canal.serverMode配置项指定为kafka,可选值有tcp、kafka和rocketmq(master分支或者最新的的v1.1.5-alpha-1版本,可以选用rabbitmq),默认是kafka。
- canal.mq.servers配置需要指定为Kafka服务或者集群Broker的地址,这里配置为127.0.0.1:9092。
canal.mq.servers在不同的canal.serverMode有不同的意义。kafka模式下,指Kafka服务或者集群Broker的地址,也就是bootstrap.servers rocketmq模式下,指NameServer列表 rabbitmq模式下,指RabbitMQ服务的Host和Port
其他配置项可以参考下面两个官方Wiki的链接:
- Canal-Kafka-RocketMQ-QuickStart
- AdminGuide
instance.properties一般指一个数据库实例的配置,Canal架构支持一个Canal服务实例,处理多个数据库实例的binlog异步解析。instance.properties需要修改的配置项主要包括:
-
canal.instance.mysql.slaveId需要配置一个和Master节点的服务ID完全不同的值,这里笔者配置为654321。
-
配置数据源实例,包括地址、用户、密码和目标数据库:
- canal.instance.master.address,这里指定为127.0.0.1:33065。
- canal.instance.dbUsername,这里指定为root。
- canal.instance.dbPassword,这里指定为root。
- 新增canal.instance.defaultDatabaseName,这里指定为test(需要在MySQL中建立一个test数据库,见前面的流程)。
-
Kafka相关配置,这里暂时使用静态topic和单个partition:
- canal.mq.topic,这里指定为test,也就是解析完的binlog结构化数据会发送到Kafka的命名为test的topic中。
- canal.mq.partition,这里指定为0。
配置工作做好之后,可以启动Canal服务:
sh /data/canal/bin/startup.sh
# 查看服务日志
tail -100f /data/canal/logs/canal/canal
# 查看实例日志 -- 一般情况下,关注实例日志即可
tail -100f /data/canal/logs/example/example.log
启动正常后,见实例日志如下:
在test数据库创建一个订单表,并且执行几个简单的DML:
use `test`;
CREATE TABLE `order`
(
id BIGINT UNIQUE PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
order_id VARCHAR(64) NOT NULL COMMENT '订单ID',
amount DECIMAL(10, 2) NOT NULL DEFAULT 0 COMMENT '订单金额',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
UNIQUE uniq_order_id (`order_id`)
) COMMENT '订单表';
INSERT INTO `order`(order_id, amount) VALUES ('10086', 999);
UPDATE `order` SET amount = 10087 WHERE order_id = '10086';
DELETE FROM `order` WHERE order_id = '10086';
这个时候,进入容器的kafka-console-consumer或者Kafka Tools查看test这个topic的数据
docker exec -it be206dc159e6 /bin/bash
sh ./bin/kafka-console-consumer.sh --bootstrap-server 127.0.0.1:9092 --from-beginning --topic test
可见Kafka的名为test的topic已经写入了对应的结构化binlog事件数据,可以编写消费者监听Kafka对应的topic然后对获取到的数据进行后续处理。
搭建canal
拉取代码 https://github.com/alibaba/canal/releases/tag/canal-1.1.4
- 配置
application.yml
文件
server:
port: 8081
logging:
level:
org.springframework: INFO
com.alibaba.otter.canal.client.adapter.hbase: DEBUG
com.alibaba.otter.canal.client.adapter.es: DEBUG
com.alibaba.otter.canal.client.adapter.rdb: DEBUG
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null
canal.conf:
mqServers: 192.168.56.119:9092 #or rocketmq
flatMessage: true
batchSize: 500
syncBatchSize: 1000
retries: 0
timeout:
accessKey:
secretKey:
mode: kafka # tcp kafka rocketMQ
srcDataSources:
demoSrc:
url: jdbc:mysql://192.168.56.119:33065/test?useUnicode=true
username: root
password: root
canalAdapters:
- instance: test # canal instance Name or mq topic name
groups:
- groupId: test
outerAdapters:
- name: logger
- name: rdb
key: demoDes
properties:
jdbc.driverClassName: com.mysql.jdbc.Driver
jdbc.url: jdbc:mysql://{
IP}:3306/test?useUnicode=true
jdbc.username: root
jdbc.password: 1234qwer
*配置demo.yml
文件
dataSourceKey: demoSrc
destination: test
outerAdapterKey: demoDes
concurrent: true
groupId: test
dbMapping:
database: test
table: order
targetTable: test.order
targetPk:
id: id
mapAll: true
- 启动
com.alibaba.otter.canal.adapter.launcher.CanalAdapterApplication
,观察目的数据库。