MySQL 使用canal整合RabbitMQ进行数据同步

1,285 阅读4分钟

# MySQL 使用canal整合RabbitMQ进行数据同步

应用场景

工作中,我们常常能遇到异构数据的同步问题,最典型的就是缓存一致性问题。之前我们需要在更新数据库后执行删除缓存操作,而这部分代码往往是高度耦合的。

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

访问http://localhost:15672

  • 创建交换机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;

# 创建一个userCREATE TABLE `user` (
   `id` int NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT '主表id',
   `name` varchar(36NOT 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"
}

image.png

至此,说明我们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更新消息。有任何想法,欢迎评论区留言,感谢阅读!