Canal 解析与 Spring Boot 整合实战

一、Canal 简介

1.1 Canal 是什么?

Canal 是阿里巴巴开源的一款基于 MySQL 数据库增量日志解析(Binlog)中间件,它模拟 MySQL 的从机(Slave)行为,监听 MySQL 主机的二进制日志(Binlog),并解析出数据变更事件(DML 和 DDL),然后将这些事件转发给下游应用,从而实现数据的实时同步和处理。

1.2 Canal 的应用场景

  • 数据同步:将 MySQL 数据库的变更数据实时同步到其他存储系统,如 Redis、Elasticsearch 等。

  • 数据备份:实时备份 MySQL 数据库的变更数据,用于数据恢复或异地备份。

  • 数据一致性:在分布式系统中,保证 MySQL 数据库和缓存(如 Redis)的数据一致性。

  • 业务解耦:通过消息队列(如 Kafka)将数据变更事件传递给下游应用,实现业务系统的解耦。

二、Canal 工作原理

2.1 MySQL 主从复制原理

在介绍 Canal 的工作原理之前,我们先复习一下 MySQL 的主从复制原理。MySQL 主从复制主要涉及以下几个步骤:

  1. 主库记录二进制日志:主库将数据变更操作(如 INSERT、UPDATE、DELETE)记录到二进制日志(Binlog)中。

  2. 从库读取二进制日志:从库连接到主库,请求二进制日志,并将日志内容写入到本地的中继日志(Relay Log)中。

  3. 从库应用中继日志:从库的 SQL 线程读取中继日志,并将日志中的数据变更操作应用到从库的数据库中。

2.2 Canal 模拟从库行为

Canal 模拟了 MySQL 从库的行为,通过以下步骤实现数据的监听和解析:

  1. 连接到 MySQL 主库:Canal 以从库的身份连接到 MySQL 主库,并请求二进制日志。

  2. 解析二进制日志:Canal 解析二进制日志中的数据变更事件(DML 和 DDL),并将其转换为 Canal 自定义的事件格式。

  3. 转发数据变更事件:Canal 将解析后的数据变更事件转发给下游应用,如 Redis、Kafka 等。

2.3 Canal 的核心组件

  • Canal Server:负责与 MySQL 主库建立连接,监听二进制日志,并解析数据变更事件。

  • Canal Client:负责接收 Canal Server 转发的数据变更事件,并进行相应的处理。

  • Canal Filter:用于过滤二进制日志中的数据变更事件,只处理感兴趣的表和字段。

三、Canal 安装与配置

3.1 开启 MySQL 二进制日志

在使用 Canal 之前,需要确保 MySQL 的二进制日志已经开启。可以通过以下命令查看二进制日志是否开启:

复制代码
SHOW VARIABLES LIKE 'log_bin';

如果 log_bin 的值为 OFF,则需要在 MySQL 配置文件中开启二进制日志:

复制代码
[mysqld]
log-bin=mysql-bin

3.2 创建 Canal 用户

为了保证数据安全,建议为 Canal 创建一个专用的 MySQL 用户,并授予其必要的权限:

复制代码
CREATE USER canal@'%' IDENTIFIED BY 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT, SUPER ON *.* TO 'canal'@'%';
ALTER USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal';
FLUSH PRIVILEGES;

3.3 使用 Docker 安装 Canal

Canal 提供了 Docker 镜像,可以通过以下命令安装并启动 Canal:

复制代码
docker pull canal/canal-server:v1.1.5

docker run -p 11111:11111 --name canal \
-e canal.destinations=tingshuTopic \
-e canal.instance.master.address=192.168.200.130:3306  \
-e canal.instance.dbUsername=canal  \
-e canal.instance.dbPassword=canal  \
-e canal.instance.connectionCharset=UTF-8 \
-e canal.instance.tsdb.enable=true \
-e canal.instance.gtidon=false  \
-e canal.instance.filter.regex=.*\\..* \
-d canal/canal-server:v1.1.5

四、Spring Boot 整合 Canal

4.1 创建 Spring Boot 工程

使用 Spring Initializr 创建一个新的 Spring Boot 工程

4.2 添加依赖

pom.xml 文件中添加以下依赖:

  • 目前canal不支持jdk17,变成jdk8版本

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    复制代码
    <groupId>com.atguigu</groupId>
    <artifactId>service-cdc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <name>service-cdc</name>
    <url>http://maven.apache.org</url>
    
    <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.3.6.RELEASE</version>
      <relativePath/>
    </parent>
    
    <properties>
      <maven.compiler.source>8</maven.compiler.source>
      <maven.compiler.target>8</maven.compiler.target>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
      <!--web 需要启动项目-->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      <dependency>
        <groupId>top.javatool</groupId>
        <artifactId>canal-spring-boot-starter</artifactId>
        <version>1.2.1-RELEASE</version>
      </dependency>
    复制代码
      <dependency>
        <groupId>javax.persistence</groupId>
        <artifactId>persistence-api</artifactId>
        <version>1.0</version>
      </dependency>
      <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency>
    </dependencies>
    </project>

4.3 配置 Canal 和 Redis

一个canal服务器有很多个客户端,每个客户端有自己的通道名字

application-dev.yml 文件中配置 Canal 和 Redis 的连接信息:

复制代码
server:
  port: 7080
#canal配置
canal:
  destination: tingshuTopic #Canal服务端发送数据的话题名称跟上面容器里参数destinations的一样
  server: 192.168.200.130:11111

spring:
  redis:
    host: 192.168.200.130
    port: 6379

4.4 创建实体类

通过实体类监听到mysql变化的数据,但因为不同表的数据都不一样,所以每个实体类的字段都不一样,但是每个表都会有id,所以在实体类中加上变化字段的id

java 复制代码
import lombok.Data;
import javax.persistence.Column;

@Data
public class CDCEntity {

// 注意Column 注解必须是persistence包下的,表示监听表中的一个字段
    @Column(name = "id")
    private Long id;
}

4.5 创建 Canal 处理类

创建一个类实现 EntryHandler 接口,用于处理 Canal 解析的数据变更事件:它定义了三个方法,分别对应 INSERTUPDATEDELETE 操作:

java 复制代码
import com.alibaba.otter.canal.client.adapter.support.EntryHandler;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

@Slf4j
@Component
@CanalTable("album_info")
public class AlbumInfoCdcHandler implements EntryHandler<CDCEntity> {

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void insert(CDCEntity cdcEntity) {
        log.info("监听到数据添加,ID: {}", cdcEntity.getId());
    }

    @Override
    public void update(CDCEntity before, CDCEntity after) {
        log.info("监听到数据更新,ID: {}", after.getId());
        String key = "album:info:" + after.getId();
        redisTemplate.delete(key);
    }

    @Override
    public void delete(CDCEntity cdcEntity) {
        log.info("监听到数据删除,ID: {}", cdcEntity.getId());
    }
}

实例:

java 复制代码
@Slf4j
@Component
@CanalTable("album_info") 监听变更表
public class AlbumInfoCdcHandler implements EntryHandler<CDCEntity> {

    @Autowired
    private RedisTemplate redisTemplate;
    
    //mysql执行添加操作,这个方法执行
    public void insert(CDCEntity cdcEntity) {
        log.info("监听到数据修改,ID:{}", cdcEntity.getId());
    }

    //mysql执行修改操作,这个方法执行
    public void update(CDCEntity before, CDCEntity after) {
        log.info("监听到数据修改,ID:{}", after.getId());
        String key = "album:info:" + after.getId();
        redisTemplate.delete(key);
    }

    //mysql执行删除操作,这个方法执行
    public void delete(CDCEntity cdcEntity) {
        log.info("监听到数据修改,ID:{}", cdcEntity.getId());
    }
}

五、总结

Canal 是一款强大的 MySQL 数据库增量日志解析中间件,通过模拟 MySQL 从库的行为,实现数据的实时同步和处理。在本文中,我们详细介绍了 Canal 的工作原理、安装配置方法以及如何与 Spring Boot 进行整合。通过 Canal,我们可以轻松地实现 MySQL 数据库与 Redis 等其他存储系统的数据一致性,为分布式系统的开发提供了有力的支持。

希望本文对你有所帮助。如果有任何问题或建议,欢迎在评论区留言。

相关推荐
Asthenia041216 分钟前
面试复盘:Spring Boot 的核心注解及其子注解
后端
Asthenia041216 分钟前
Spring Boot 的Scope:Singleton/Prototype/Request/Session/Application/WebSocket
后端
小奏技术1 小时前
写了一个Java(mybatis plus)代码生成器,但是是GO实现的
后端
uhakadotcom1 小时前
使用PySpark与阿里云MaxCompute的简单指南
后端·面试·github
小杨4041 小时前
springboot框架项目实践应用十(拦截器+redis实现接口防刷)
spring boot·后端·架构
可乐加.糖1 小时前
Java 分布式高并发重试方案及实现
java·开发语言·spring boot·redis·分布式
沉默的煎蛋1 小时前
深入理解 TCP 三次握手与四次挥手
java·网络·数据结构·网络协议·tcp/ip
猿来入此小猿1 小时前
基于SpringBoot+Vue3实现的宠物领养管理平台功能七
java·spring boot·毕业设计·宠物·宠物领养·毕业源码·免费学习
沉默的煎蛋2 小时前
深入理解计算机网络:OSI 与 TCP/IP 各层结构与功能
java·linux·服务器·开发语言·网络·tcp/ip·计算机网络
星星不打輰2 小时前
Java集合基础知识
java