应用场景
工作中,我们常常能遇到异构数据的同步问题,最典型的就是缓存一致性问题。之前我们需要在更新数据库后执行删除缓存操作,而这部分代码往往是高度耦合的。
canal是阿里巴巴开源的MySQL binlog 增量订阅&消费组件。它的原理是伪装成MySQL的从库来监听主库的binlog。因此,我们可以使用canal+MQ的方式把更新数据库和删除缓存进行解耦。
官网:github.com/alibaba/can…
接下来,我们会在docker中进行实践。
安装RabbitMQ
拉取RabbitMQ镜像
sudo docker pull rabbitmq
启动RabbitMQ容器
sudo docker run --name rabbitmq --hostname rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
-d rabbitmq
- 创建交换机exchange.canal
- 创建队列canal-example
- 绑定队列,路由key为canal-routing-key
踩坑:
打开管理界面,发现修改exchange报错:rabbitmq: Management API returned status code 500
解决办法:
# 进入rabbitmq容器
sudo docker exec -it rabbitmq bash
# 修改配置文件
cd /etc/rabbitmq/conf.d/
echo management_agent.disable_metrics_collector = false > management_agent.disable_metrics_collector.conf
修改完配置文件后,重新启动容器即可。
sudo docker restart rabbitmq
安装canal
在安装canal之前,我们需要在MySQL中授权 canal 链接 MySQL 的账号具有作为 MySQL slave 的权限。
# 进入mysql容器
sudo docker exec -it mysql5.7-master bash
# 创建canal用户
CREATE USER canal IDENTIFIED BY 'canal';
# 授予权限
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
# 刷新权限
FLUSH PRIVILEGES;
创建完canal用户后,我们拉取canal镜像。
sudo docker pull canal/canal-server:v.1.15
启动canal容器,copy出配置文件
#启动镜像
sudo docker run --name canal -d canal/canal-server
#进入容器 查看配置文件路径
docker exec -it canal bash
#找到文件位置后 exit退出容器 将容器内部文件copy到外部
docker cp canal:/home/admin/canal-server/conf/canal.properties /usr/local/docker/canal-server/conf
docker cp canal:/home/admin/canal-server/conf/example/instance.properties /usr/local/docker/canal-server/conf
修改canal.properties文件中的以下地方
# tcp, kafka, rocketMQ, rabbitMQ, pulsarMQ
canal.serverMode = rabbitMQ
##################################################
######### RabbitMQ #############
##################################################
rabbitmq.host = xxxxxx # xxxxxx是rabbitmq容器ip
rabbitmq.virtual.host = /
rabbitmq.exchange = exchange.canal
rabbitmq.username = guest
rabbitmq.password = guest
rabbitmq.deliveryMode =
ps: 我们可以使用以下命令查询出docker相关容器的ip
sudo docker inspect --format='{{.NetworkSettings.IPAddress}}' 容器
修改instance.properties文件中的以下地方
# position info xxxxxx是mysql公网ip
canal.instance.master.address=xxxxxx:3306
# username/password
canal.instance.dbUsername=canal # mysql主库中创建的canal用户
canal.instance.dbPassword=canal # mysql主库中创建的canal用户的密码
canal.instance.connectionCharset = UTF-8
# enable druid Decrypt database password
canal.instance.enableDruid=false
# table regex 过滤规则,这里使用默认的
canal.instance.filter.regex=.*\..*
# mq config
canal.mq.topic=canal-routing-key # rabbitmq中配置的路由key
修改完配置文件后,我们需要停止并删除刚才启动的canal容器,再使用外部配置文件的方式启动canal。
# 停止容器
sudo docker stop canal
# 删除容器
sudo docker rm canal
# 重新启动容器
sudo docker run --name canal \
> -p 11111:11111 \
> -v /usr/local/docker/canal-server/conf/instance.properties:/home/admin/canal-server/conf/example/instance.properties \
> -v /usr/local/docker/canal-server/conf/canal.properties:/home/admin/canal-server/conf/canal.properties \
> -v /usr/local/docker/canal-server/logs:/home/admin/canal-server/logs/ \
> -d canal/canal-server:v1.1.5
在/usr/local/docker/canal-server/logs/canal/下打开canal.log文件,显示以下日志表示启动成功。
2022-08-23 13:10:04.426 [main] INFO com.alibaba.otter.canal.deployer.CanalLauncher - ## set default uncaught exception handler
2022-08-23 13:10:04.444 [main] INFO com.alibaba.otter.canal.deployer.CanalLauncher - ## load canal configurations
2022-08-23 13:10:04.508 [main] INFO com.alibaba.otter.canal.deployer.CanalStarter - ## start the canal server.
2022-08-23 13:10:04.527 [main] INFO com.alibaba.otter.canal.deployer.CanalController - ## start the canal server[172.17.0.5(172.17.0.5):11111]
2022-08-23 13:10:05.231 [main] INFO com.alibaba.otter.canal.deployer.CanalStarter - ## the canal server is running now ......
验证
现在我们需要在MySQL中执行一条insert语句,看看RabbitMQ中是否能收到消息。
# 进入mysql容器
sudo docker exec -it mysql5.7-master bash
# 进入容器连接mysql后执行
# 使用go_web数据库
use go_web;
# 创建一个user表
CREATE TABLE `user` (
`id` int NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '主表id',
`name` varchar(36) NOT NULL COMMENT '名称'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
# 插入一个user
INSERT INTO `user`(`name`) VALUES('cyj19');
我们可以在RabbitMQ的管理界面查看到有消息过来,消息为:
{
"data": [{
"id": "6",
"name": "cyj19"
}],
"database": "go_web",
"es": 1661235701000,
"id": 3,
"isDdl": false,
"mysqlType": {
"id": "int(11)",
"name": "varchar(36)"
},
"old": null,
"pkNames": ["id"],
"sql": "",
"sqlType": {
"id": 4,
"name": 12
},
"table": "user",
"ts": 1661235701370,
"type": "INSERT"
}
至此,说明我们canal+MQ已经成功搭建了。
踩坑:
问题:MySQL更新数据后,RabbitMQ中没有收到消息。
首先有可能是canal没有成功监听到biglog,打开/usr/local/docker/canal-server/logs/example下的example.log,发现报了1146错误。
com.alibaba.otter.canal.parse.exception.CanalParseException: java.io.IOException: ErrorPacket [errorNumber=1146, fieldCount=-1, message=Table 'go_web.BASE TABLE' doesn't exist, sqlState=42S02, sqlStateMarker=#]
之后在官网的issues中找到原因(canal最新版本的instance.properties中使用默认的regex会导致该问题)
详见:github.com/alibaba/can…
解决办法:我们降低版本,改用canal/canal-server:v1.1.15即可
总结
以上详细说明如何搭建canal+MQ来订阅MySQL更新消息。有任何想法,欢迎评论区留言,感谢阅读!