文章目录
- 一、Canal
- 二、前期准备
-
- [2.1 检查MySQL是否开启binlog](#2.1 检查MySQL是否开启binlog)
- [2.2 下载canal](#2.2 下载canal)
- [2.2 修改Canal的配置文件](#2.2 修改Canal的配置文件)
- [2.3 查看canal是否启动](#2.3 查看canal是否启动)
- [2.4 RabbitMQ队列创建](#2.4 RabbitMQ队列创建)
-
- [添加交换机 canal.exchange](#添加交换机 canal.exchange)
- [添加队列 canal.queue](#添加队列 canal.queue)
- 队列绑定交换机
- [三、SpringBoot项目中集成 Canal + RabbitMQ](#三、SpringBoot项目中集成 Canal + RabbitMQ)
一、Canal
在工作中,我们常常会遇到同步MySQL数据到Redis的需求,我们也会有很多种方式去解决,常见的方式有如下几种
- 业务操作中同步
- 使用消息队列
- 基于binlog方式
基于binlog方式

其中Mysql主从工作原理:
Mysql主从,根据2/8原则,80%的性能问题都在读上面,当我们数据库的读并发较大的时候,我们可以使用Mysql主从来分担读的压力。它的原理是所有的写操作在主库上,读操作在从库上,当然主库也可以承担读请求,而从库的数据则通过主库复制而来。

- MySQL master 将数据变更写入二进制日志( binary log, 其中记录叫做二进制日志事件binary log events,可以通过 show binlog events 进行查看)
- MySQL slave 将 master 的 binary log events 拷贝到它的中继日志(relay log)
- MySQL slave 重放 relay log 中事件,将数据变更反映它自己的数据
主从复制步骤:
- 将Master的binary-log日志文件打开,mysql会把所有的DDL,DML,TCL写入BinaryLog日志文件中
- Master会生成一个 log dump 线程,用来给从库的 i/o线程传binlog
- 从库的i/o线程去请求主库的binlog,并将得到的binlog日志写到中继日志(relaylog)中
- 从库的sql线程,会读取relaylog文件中的日志,并解析成具体操作,通过主从的操作一致,而达到最终数据一致
- 而Canal的工作原理就是伪装成mysql的从库去获取Binlog中SQL语句再更新到Redis。
二、前期准备
2.1 检查MySQL是否开启binlog
binlog是二进制日志文件,用于记录mysql的数据变更,数据在恢复的时候binlog日志起着至关重要的作用
sql
#cmd打开命令提示符
#输入该代码登录mysql
mysql -u root -p
#执行命令查看 binlog 状态 Value 为 ON,则表示 binlog 已开启
SHOW VARIABLES LIKE 'log_bin';
#执行命令查看 binlog 格式 如果 Value 为 ROW,则表示 binlog 格式为 ROW,这是 Canal 正常工
作所必需的格式
SHOW VARIABLES LIKE 'binlog_format';

若没有开启binlog,则需要进行配置,配置步骤如下:
找到my.ini文件,默认位置: C:\ProgramData\MySQL\MySQL Server 8.0
如果在安装MySQL时使用的是自定义安装,请看你安装位置的文件中的my.ini文件
bash
#在mysqld下面添加
server_id=1918
log_bin = mysql-bin
binlog_format = ROW

重启MySQL服务,并查看是否开启binlog,查看方式同上
创建用户,方便后续测试
bash
# 使用命令登录:mysql -u root -p 或 在客户端软件操作
# 创建用户 用户名:canal 密码:canal
# 注意:myql8.0版本的密码加密方式为caching_sha2_password,如果不做处理在canal连接时会提示连接不上或没有权限
# 所以我们修改为mysql_native_password
create user 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal';
# 授权 *.*表示所有库
grant SELECT, REPLICATION SLAVE, REPLICATION CLIENT on *.* to 'canal'@'%';
#刷新权限
FLUSH PRIVILEGES;
执行完上面的语句后查看所有用户
bash
select * from mysql.user;

2.2 下载canal
Canal仓库地址 : https://github.com/alibaba/canal/releases


解压后文件目录:

2.2 修改Canal的配置文件
修改instance.properties
修改instance 配置文件 : conf/example/instance.properties
bash
# 按需修改成自己的数据库信息
#################################################
canal.instance.master.address=127.0.0.1:3380
# username/password,数据库的用户名和密码
...
#刚才开通的mysql的账户密码
canal.instance.dbUsername = canal
canal.instance.dbPassword = canal
.......
# table regex 将canal.instance.filter.regex=.*\\..* 改为 canal.instance.filter.regex=canaldb\\..*
canal.instance.filter.regex=canaldb\\..*
...
#################################################
# mq config 数据同步到MQ中的topic名字
canal.mq.topic=canal.exchange
# 添加账号密码
canal.mq.username=guest
canal.mq.password=guest
# 针对库名或者表名发送动态topic
#canal.mq.dynamicTopic=mytest,.*,mytest.user,mytest\..*,.*\..*
canal.mq.partition=0
# hash partition config
#canal.mq.partitionsNum=3
#库名.表名: 唯一主键,多个表之间用逗号分隔
#canal.mq.partitionHash=mytest.person:id,mytest.role:id
修改canal.properties
修改canal 配置文件 conf/canal.properties
bash
canal.ip = 0.0.0.0
# register ip to zookeeper
canal.register.ip = 127.0.0.1
# 添加下面这一条 确保实例加载配置正确(默认加载 conf 目录下的所有实例文件夹)
canal.destinations = example
# ...
# 可选项: tcp(默认) tcp, kafka, rocketMQ, rabbitMQ
# 这里使用rabbitMQ
canal.serverMode = tcp
##################################################
######### RabbitMQ #############
##################################################
# rabbitMQ所在主机地址
rabbitmq.host = 127.0.0.1:5672
rabbitmq.virtual.host = /
# 交换机
rabbitmq.exchange = canal.exchange
# 用户名
rabbitmq.username = guest
# 密码
rabbitmq.password = guest
rabbitmq.deliveryMode =
修改startup.bat
修改bin/startup.bat
因为我们使用的Java17 版本过高,不再支持 -XX:PermSize参数。这个参数在 Java 8 及以后版本已被移除。

修改完毕后,启动canal,找到bin/startup.bat 启动canal

2.3 查看canal是否启动
注意:下面三步必须全对才算启动canal成功
- 查看
logs\canal\canal.log日志是否最近修改时间是你启动的时间。

- 查看
logs\example\example.log查看是否报错,可以按住Ctrl+F搜一下是否有ERROR,如果没有说明启动成功。

- 新打开命令行窗口执行
netstat -ano | findstr :11111指令查看是否启动,如果出现以下情况说明启动成功。

2.4 RabbitMQ队列创建
添加交换机 canal.exchange

添加队列 canal.queue

队列绑定交换机
点进去队列

输入canal.exchange和canal.routing.key

三、SpringBoot项目中集成 Canal + RabbitMQ
创建项目


pom文件中引入依赖
xml
<!-- Canal 协议依赖 -->
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.protocol</artifactId>
<version>1.1.6</version>
</dependency>
<!-- Canal 公共组件 -->
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.common</artifactId>
<version>1.1.6</version>
</dependency>
<!-- Canal 客户端依赖 -->
<dependency>
<groupId>com.alibaba.otter</groupId>
<artifactId>canal.client</artifactId>
<version>1.1.6</version>
</dependency>
<!-- Spring Boot 核心启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 消息队列 AMQP 支持(如 RabbitMQ) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- 阿里巴巴 FastJSON2 (JSON处理) -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.31</version>
</dependency>
<!-- 测试依赖(解决 JUnit 和 Spring Boot Test 缺失问题) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
yml配置
yml
spring:
# 数据库连接配置(Canal需要连接数据库获取binlog)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3380/smbms?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
username: root # 数据库用户名(需要有binlog权限)
password: root # 数据库密码
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtual-host: /
redis:
host: 127.0.0.1
port: 6379
database: 0
password: 123456
# Canal客户端配置(监听数据库binlog)
canal:
client:
instances:
example: # 实例名,可自定义
host: 192.168.108.13 # Canal Server地址(如果是本地部署的Canal服务)
port: 11111 # Canal默认端口
username: canal # Canal默认用户名
password: canal # Canal默认密码
filter: smbms.* # 监听的数据库表(格式:库名.表名,*表示所有表)
# 日志配置
logging:
level:
com.yak: DEBUG
org.springframework.amqp: INFO
com.alibaba.otter.canal: INFO # 开启Canal日志,方便调试
CanalRabbitMQConsumer
java
package com.hsh.canal.listen;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
@Component
public class CanalRabbitMQConsumer {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 直接监听Canal通过RabbitMQ发送的消息
* Canal会直接将binlog数据发送到配置的exchange中
*/
@RabbitListener(queues = "canal.queues")
public void processCanalMessage(String message) {
System.out.println("收到CALAL数据: " + message);
// 存储到Redis
redisTemplate.opsForValue().set("canal:lastChange", message);
System.out.println("已同步到Redis: " + message);
// 这里可以添加具体的业务处理逻辑
// 比如解析JSON、更新缓存、同步到其他系统等
}
}
RabbitConfig
java
package com.hsh.canal.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
@Bean
public Queue canalSyncQueue() {
return new Queue("canal.queues", true);
}
@Bean
public DirectExchange canalExchange() {
return new DirectExchange("canal.exchange", true, false);
}
// 修改绑定,使用空路由键
@Bean
public Binding bindingCanalQueue(Queue canalSyncQueue, DirectExchange
canalExchange) {
return BindingBuilder.bind(canalSyncQueue)
.to(canalExchange)
.with(""); // 改为空路由键,匹配所有消息
}
}
我这demo只要运行起来不报错说明canal能用,但是没有说canal怎么用,我会在另一篇帖子说明。