springboot整合canal

学习链接

Cannal项目地址

SpringBoot整合Canal实现数据同步到ElasticSearch - 原文地址

Spring Boot整合canal实现数据一致性解决方案解析-部署+实战

Java:SpringBoot整合Canal实现数据同步

docker环境安装mysql、canal、elasticsearch,基于binlog利用canal实现mysql的数据同步到elasticsearch中

canal Value too long for column "CHARACTER VARYING"

canal学习笔记

文章目录

  • 学习链接
    • SpringBoot整合Canal实现数据同步
      • 一、前言
      • 二、Canal简介
      • 三、MySQL开启BinLog日志
      • 四、Canal的配置和启动
        • [4.1 下载Canal](#4.1 下载Canal)
        • [4.2 修改canal配置](#4.2 修改canal配置)
        • [4.3 启动Canal](#4.3 启动Canal)
      • 五、搭建Canal-Admin可视化管理
        • [5.1 下载Canal-admin](#5.1 下载Canal-admin)
        • [5.2 修改配置文件](#5.2 修改配置文件)
        • [5.3 启动并登录](#5.3 启动并登录)
        • [5.4 配置canal-server和canal-admin](#5.4 配置canal-server和canal-admin)
      • 六、SpringBoot整合Canal
        • [6.1 项目添加依赖](#6.1 项目添加依赖)
        • [6.2 新建监听类](#6.2 新建监听类)
        • [6.3 配置启动时开启监听](#6.3 配置启动时开启监听)
        • [6.4 启动canal服务,并修改数据](#6.4 启动canal服务,并修改数据)
      • [七、 SpringBoot+RabbitMQ+Canal监听MySQL数据变化](#七、 SpringBoot+RabbitMQ+Canal监听MySQL数据变化)
        • [7.1 搭建rabbitMq](#7.1 搭建rabbitMq)
        • [7.2 配置RabbitMQ](#7.2 配置RabbitMQ)
        • [7.3 修改canal配置](#7.3 修改canal配置)
        • [7.4 SpringBoot 整合 RabbitMQ](#7.4 SpringBoot 整合 RabbitMQ)
        • [7.5 运行结果](#7.5 运行结果)

SpringBoot整合Canal实现数据同步

一、前言

二、Canal简介

canal 译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费,canal可以用来监控数据库数据的变化,从而获得新增数据,或者修改的数据。canal原理就是伪装成mysql的从节点,从而订阅master节点的binlog日志

Canal原理:

  1. canal模拟mysql slave的交互协议,伪装自己为mysql slave,向mysql master发送dump协议
  2. mysql master收到dump请求,开始推送binary log给slave(也就是canal)
  3. canal解析binary log对象(原始为byte流)

三、MySQL开启BinLog日志

注意: 由于不同环境,不同版本的MySQL可能存在差异,如果不能按下述方法开启binlog日志,那么就自行百度开启binlog步骤

3.1 检查MySQL是否有开启binlog日志
mysql 复制代码
show variables like 'log_bin';

如果显示如下,则代表未开启binlog日志

3.2开启binlog日志步骤
mysql 复制代码
#1.编辑MySQL配置文件(linux下的修改命令)
vim /etc/my.cnf
#2.添加配置
[mysqld]
log-bin=mysql-bin # 开启binlog
binlog-format=ROW # 选择ROW模式
server_id=1 # 配置MySQL replaction需要定义,不和Canal的slaveId重复即可
#3.重启MySQL,再次使用下述命令查看是否开启binlog
show variables like 'log_bin';
show binary logs;
show variables like 'binlog_format%'

windows环境下:

重启MySQL服务:

再次使用命令查看是否已经开启binlog日志,ON代表已经成功开启

**提示:**binlog文件的位置:如果在修改my.ini的binlog时给的是全路径,那么生成的日志文件就在指定的目录下;如果添加配置时只给一个名字,那么生成的binlog日志的位置为

四、Canal的配置和启动

4.1 下载Canal

Canal的下载:

进入下载地址,选择Canal下载,我这里下载的是windows版本,如果你是linux版本的可以选择不同的版本下载,也可以使用docker搭建

4.2 修改canal配置
shell 复制代码
# position info 你的MySQL的主机和端口
canal.instance.master.address=127.0.0.1:3306
# username/password 连接MySQL的用户名和密码
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal
canal.instance.connectionCharset = UTF-8
4.3 启动Canal

在canal.deployer-1.1.7-SNAPSHOT\bin目录下,双击startup.bat即可启动

查看是否启动成功:

看到上述截图,代表Canal的服务端已经搭建成功

五、搭建Canal-Admin可视化管理

5.1 下载Canal-admin

下载地址:https://github.com/alibaba/canal/releases

5.2 修改配置文件
5.3 启动并登录

通过双击bin目录下的startup.bat文件启动canal-admin,然后通过IP+8089端口访问

使用密码登录:默认用户 admin/123456

5.4 配置canal-server和canal-admin

具体不做描述,不是本篇重点,后续补充

六、SpringBoot整合Canal

注意: 项目pom文件中导入的canal版本需要和本地(或服务器)上启动的canal版本保持一致,否则会有代码中连接不上本地canal的情况,本案例采用的是canal 1.6版本

6.1 项目添加依赖
java 复制代码
	<!--canal-->
     <dependency>
        <groupId>com.alibaba.otter</groupId>
        <artifactId>canal.client</artifactId>
        <version>1.1.6</version>
     </dependency>
       <!--如果你用的是canal1.1.6版本以下的,可以不用映入下面的这项内容,用1.1.6不引入这个会导入不了Message类报错--> 
    <dependency>
        <groupId>com.alibaba.otter</groupId>
        <artifactId>canal.protocol</artifactId>
        <version>1.1.6</version>
     </dependency>
6.2 新建监听类

新建监听类,用于监听Canal通道中的binlog日志信息,实时监听数据库的数据变化,代码如下:

java 复制代码
package com.study.listener;

import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.util.List;

/**
 * @BelongsProject: workdemo
 * @BelongsPackage: com.study.listener
 * @Author: jiaoqixue
 * @CreateTime: 2022-12-22  14:27
 * @Description: TODO
 * @Version: 1.0
 * 这种没有经过消息队列
 * 直接连接Canal
 * 每隔一条去检查Canal里有没有消息
 */
@Slf4j
@Component
public class CanalClient {
    private final static int BATCH_SIZE = 1000;

    /**
     * @param
     * @Author: jqx
     * @Date: 2022/12/22 15:11
     * @Description: Canal入库方法
     */
    public void run() {
        //建立连接
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("10.0.42.83", 11111), "example", "", "");
        try {
            //打开连接(注意pom文件中引入的canal版本一定要和本机启动的版本保持一致,否则可能会出现连接打开被拒绝的情况)
            connector.connect();
            //配置需要监听的数据表(订阅数据库表,全部表)
            connector.subscribe(".*..*");
            //回滚到未ack的地方,下次fetch的时候,可以从最后一个没有ack的地方拿
            connector.rollback();
            //
            while (true) {
                //获取指定数量的数据
                Message message = connector.getWithoutAck(BATCH_SIZE);
                //获取批量ID
                long batchid = message.getId();
                //获取批量的数量
                int size = message.getEntries().size();
                //如果没有数据,线程睡眠一秒
                if (batchid == -1 || size == 0) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    //如果有数据则处理数据
                    dataHandle(message.getEntries());
                }
                //进行 batch id 的确认。确认之后,小于等于此 batchId 的 Message 都会被确认。
                connector.ack(batchid);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            connector.disconnect();
        }
    }

    /**
     * @param entrys
     * @Author: jqx
     * @Date: 2022/12/22 16:40
     * @Description: 数据处理方法
     */
    private void dataHandle(List<CanalEntry.Entry> entrys) throws Exception {
        for (CanalEntry.Entry entry : entrys) {
            if (entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONBEGIN || entry.getEntryType() == CanalEntry.EntryType.TRANSACTIONEND) {
                //开启或者关闭事务的实体类型,跳过
                continue;
            }
            //RowChange对象,包含了一行数据变化的所有特征
            CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
            //获取操作类型:insert/update/delete类型
            CanalEntry.EventType eventType = rowChange.getEventType();
            //判断是否为 DDL语句
            if (rowChange.getIsDdl()) {
                log.info("是DDL语句{}", rowChange.getSql());
            }
            // 根据不同的语句类型,处理不同的业务
            if (eventType == CanalEntry.EventType.INSERT) {
                //是新增语句,业务处理。如果新增的时候数据没有发生变化的情况下,是不会被执行
                log.info("新增数据:库名:{},--表名:{}", entry.getHeader().getSchemaName(), entry.getHeader().getTableName());

            } else if (eventType == CanalEntry.EventType.UPDATE) {
                //是修改语句,业务处理。如果修改的时候是没有修改任何数据的情况下,是不会被执行
                log.info("修改数据:库名:{},--表名:{}", entry.getHeader().getSchemaName(), entry.getHeader().getTableName());

            } else if (eventType == CanalEntry.EventType.DELETE) {
                //是删除语句,业务处理。如果删除的时候是没有数据的情况下,是不会被执行
                log.info("删除数据:库名:{},--表名:{}", entry.getHeader().getSchemaName(), entry.getHeader().getTableName());

            }
        }
    }
}
6.3 配置启动时开启监听
  1. 方式一

    在启动类上集成CommandLineRunner接口,并重写run方法,Spring boot的CommandLineRunner接口主要用于实现在项目启动后,去执行一段代码块逻辑,这段初始化代码在整个应用生命周期内只会执行一次,当项目中有多个类实现CommandLineRunner接口时,可以通过注解@Order控制实现类执行顺序,其中Order的值越小越先被执行。

    java 复制代码
    package com.study;
    
    import com.study.listener.CanalClient;
    import org.springframework.amqp.rabbit.annotation.EnableRabbit;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.annotation.EnableScheduling;
    
    import javax.annotation.Resource;
    
    @SpringBootApplication
    @EnableScheduling
    @EnableDiscoveryClient
    @EnableAsync
    @EnableRabbit
    public class WorkdemoApplication implements CommandLineRunner {
        @Resource
        private CanalClient canalClient;
        public static void main(String[] args) {
            SpringApplication.run(WorkdemoApplication.class, args);
        }
    
        @Override
        public void run(String... args) throws Exception {
            //项目启动,执行canal客户端监听,原因实现了CommandLineRunner接口,可在项目启动后执行此段代码
            canalClient.run();
        }
    }
  2. 方式二

    在监听类上实现ApplicationRunner接口,springBoot项目启动时,若想在启动之后直接执行某一段代码,就可以用 ApplicationRunner这个接口,并实现接口里面的run(ApplicationArguments args)方法,方法中写上自己的想要的代码逻辑

    java 复制代码
    @Component  //此类一定要交给spring管理
    public class ConsumerRunner implements ApplicationRunner{
    	@Override
    	public void run(ApplicationArgumers args) throws Exception{
    		//代码
    		System.out.println("需要在springBoot项目启动时执行的代码---");
    	}
    }
6.4 启动canal服务,并修改数据

七、 SpringBoot+RabbitMQ+Canal监听MySQL数据变化

7.1 搭建rabbitMq

RabbitMQ的搭建非本文重点,所以这里不做详细描述,如不能按照以下方式搭建成功,可以自己百度搭建rabbitMq,我这里使用的是docker搭建的

shell 复制代码
#1.获取镜像
docker pull rabbitmq
#2.创建并启动容器
docker run -d -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin --hostname myRabbit --name rabbitmq rabbitmq
#3.启动rabbitmq_management(RabbitMQ的管理界面)
docker exec -it rabbitmq rabbitmq-plugins enable rabbitmq_management
#4.开放服务器的15672端口和5672端口
firewall-cmd --zone=public --add-port=15672/tcp --permanent
firewall-cmd --zone=public --add-port=5672/tcp --permanent
#5.重启防火墙生效
firewall-cmd --reload
#6.检查阿里云安全组是否放行对应的端口,如果没有放行也是无法访问的,记得去阿里云控制台去放行对应的端口
#7.访问http://ip:15672
#用户名:admin
#密码:admin
7.2 配置RabbitMQ
  • 创建交换机

  • 创建队列

  • 绑定交换机和队列

7.3 修改canal配置
  1. 修改canal.properties文件:

    shell 复制代码
    # tcp, kafka, rocketMQ, rabbitMQ, pulsarMQ
    canal.serverMode = rabbitMQ
    
    ##################################################
    ######### 		    RabbitMQ	     #############
    ##################################################
    rabbitmq.host =IP
    #如果你没有在启动容器的时候使用RABBITMQ_DEFAULT_VHOST=my_vhost指定,那你就写 / 即可
    rabbitmq.virtual.host =my_vhost
    rabbitmq.exchange =canal.exchange
    rabbitmq.username =admin
    rabbitmq.password =admin
    rabbitmq.deliveryMode =
  2. 修改 instance.properties 文件:

    #数据库连接信息
    # position info
    canal.instance.master.address=127.0.0.1:3306
    
    # username/password
    canal.instance.dbUsername=root
    canal.instance.dbPassword=123456
    
    # mq config
    canal.mq.topic=canal.routing.key
    
  3. 修改完上述两个位置后,重启canal

7.4 SpringBoot 整合 RabbitMQ
  1. 引入maven依赖

    xml 复制代码
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
  2. 添加配置信息

    properties 复制代码
    #RabbitMQ配置
    spring.rabbitmq.host=106.122.133.533
    spring.rabbitmq.port=5672
    spring.rabbitmq.username=admin
    spring.rabbitmq.password=admin
    spring.rabbitmq.virtual-host=my_vhost
  3. 新建RabbitMQ 监听器

java 复制代码
package com.study.listener;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * Canal + RabbitMQ 监听数据库数据变化
 */
@Slf4j
@Component
public class CanalListener {


    @RabbitListener(bindings = {
            @QueueBinding(
                    value = @Queue(value = "canal.queue", durable = "true"),
                    exchange = @Exchange(value = "canal.exchange"),
                    key = "canal.routing.key"
            )
    })
    public void handleDataChange(String message) { // 这里要改成byte[]去接收
        JSONObject object = JSONObject.parseObject(message);
        log.info("Canal监听到数据发生变化\n库名:{}\n表名:{}\n类型:{}\n数据:{}", object.getString("database"), object.getString("table"), object.getString("type"), object.getString("data"));
    }
}
7.5 运行结果
启动项目,并修改一条数据库记录
相关推荐
Super丶病毒8 天前
Docker 中使用 PHP 通过 Canal 同步 Mysql 数据到 ElasticSearch
mysql·elasticsearch·docker·canal·php
idealzouhu2 个月前
【canal 中间件】canal 实时监听 binlog
mysql·canal
idealzouhu2 个月前
【Canal 中间件】Canal 实现 MySQL 增量数据的异步缓存更新
mysql·缓存·中间件·canal
idealzouhu2 个月前
【Canal 中间件】Canal使用原理与基本组件概述
mysql·canal
这孩子叫逆2 个月前
Canal 扩展篇(阿里开源用于数据同步备份,监控表和表字段(日志))
mysql·canal·日志·阿里·binary_log
cyt涛3 个月前
搜索功能技术方案
mysql·elasticsearch·全文检索·canal·索引·数据同步·搜索
cyt涛3 个月前
Canal+RabbitMQ数据同步环境配置
数据库·分布式·mysql·rabbitmq·canal·数据同步·主从同步
ZZDICT4 个月前
开源的数据库增量订阅和消费的中间件——Cancl
canal
菠菠萝宝6 个月前
【云岚到家】-day04-2-索引同步-搜索接口
java·mysql·elasticsearch·spring cloud·微服务·rabbitmq·canal