Canal安装及使用

canal可以用来监控数据库数据的变化,从而获得新增数据,或者修改的数据。
canal是应阿里巴巴存在杭州和美国的双机房部署,存在跨机房同步的业务需求而提出的。
阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&消费的业务。

1.Canal工作原理

在这里插入图片描述

原理相对比较简单:

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

canal需要使用到mysql,我们需要先安装mysql, canal是基于mysql的主从模式实现的,所以必须先开启binlog.

2.mysql开启binlog模式

(1) 连接到mysql中,并修改/etc/mysql/mysql.conf.d/mysqld.cnf 需要开启主从模式,开启binlog模式。

1.1 docker形式安装修改mysqld.cnf文件

执行如下命令,编辑mysql配置文件

在这里插入图片描述

命令行如下:

docker exec -it mysql /bin/bash
cd /etc/mysql/mysql.conf.d
vi mysqld.cnf

修改mysqld.cnf配置文件,添加如下配置:

上图配置如下:

在这里插入图片描述

binlog-format=ROW
log-bin/var/lib/mysql/mysql-bin
server-id=12345

(2) 创建账号 用于测试使用,

使用root账号创建用户并授予权限

create user canal@'%' IDENTIFIED by 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;

(3)重启mysql容器

docker restart mysql

1.2 yum形式安装的mysql

执行以下命令修改mysql配置文件(my.cnf)

vi /etc/my.cnf

与上面一样添加那三行配置→ 使用root账号为cannal创建用户并授权→重启mysql

3.canal容器安装

下载镜像:

docker pull docker.io/canal/canal-server

容器安装

docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server

进入容器,修改核心配置canal.properties 和instance.properties,canal.properties 是canal自身的配置,instance.properties是需要同步数据的数据库连接配置。

执行代码如下:

docker exec -it canal /bin/bash
cd canal-server/conf/
vi canal.properties
cd example/
vi instance.properties

修改canal.properties的id,不能和mysql的server-id重复,如下图:

在这里插入图片描述

修改instance.properties,配置数据库连接地址:

在这里插入图片描述

这里的canal.instance.filter.regex有多种配置,如下:

可以参考地址如下:

https://github.com/alibaba/canal/wiki/AdminGuide
mysql 数据解析关注的表,Perl正则表达式.
多个正则之间以逗号(,)分隔,转义符需要双斜杠(\\) 
常见例子:
1.  所有表:.*   or  .*\\..*
2.  canal schema下所有表: canal\\..*
3.  canal下的以canal打头的表:canal\\.canal.*
4.  canal schema下的一张表:canal.test1
5.  多个规则组合使用:canal\\..*,mysql.test1,mysql.test2 (逗号分隔)
注意:此过滤条件只针对row模式的数据有效(ps. mixed/statement因为不解析sql,所以无法准确提取tableName进行过滤)

配置完成后,设置开机启动,并记得重启canal。

docker update --restart=always canal
docker restart canal

4.canal微服务搭建

当用户执行 数据库的操作的时候,binlog 日志会被canal捕获到,并解析出数据。我们就可以将解析出来的数据进行同步到redis中即可。

思路:创建一个独立的程序,并监控canal服务器,获取binlog日志,解析数据,将数据更新到redis中。这样广告的数据就更新了。

(1)安装辅助jar包

github地址 https://github.com/chenqian56131/spring-boot-starter-canal

spring-boot-starter-canal-master中有一个工程starter-canal,它主要提供了SpringBoot环境下canal的支持,我们需要先安装该工程,在starter-canal目录下执行 mvn clean install -Deskiptest

可参考 https://blog.csdn.net/zbx931197485/article/details/109508738

(2)canal微服务工程搭建

在changgou-service下创建changgou-service-canal工程,并引入相关配置。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <parent>
        <artifactId>changgou-service</artifactId>
        <groupId>com.changgou</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>changgou-service-canal</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!--canal依赖-->
        <dependency>
            <groupId>com.xpand</groupId>
            <artifactId>starter-canal</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

application.yml配置

server:
  port: 18082
spring:
  application:
    name: canal
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka
  instance:
    prefer-ip-address: true
feign:
  hystrix:
    enabled: true
#hystrix 配置
hystrix:
  command:
    default:
      execution:
        timeout:
        #如果enabled设置为false,则请求超时交给ribbon控制
          enabled: true
        isolation:
          strategy: SEMAPHORE
#canal配置
canal:
  client:
    instances:
      example:
        host: 192.168.211.132
        port: 11111

(3)监听创建

创建一个CanalDataEventListener类,实现对表增删改操作的监听,代码如下:

package com.changgou.canal.listener;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.xpand.starter.canal.annotation.*;
@CanalEventListener
public class CanalDataEventListener {

    /**
    * 增加数据监听
    * @param eventType 当前操作的类型 增加数据
    * @param rowData 发生变更的一行数据
    */
   @InsertListenPoint
   public void onEventInsert (CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
       System.out.println("insertListenPoint...");
       rowData.getAfterColumnsList().forEach(
               column -> System.out.println("insert--By--Annotation: " + column.getName() + " ::   " + column.getValue())
       );
   }

   /***
    * 修改数据监听
    * @param rowData
    */
   @UpdateListenPoint
   public void onEventUpdate(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
       System.out.println("updateListenPoint...");
       rowData.getBeforeColumnsList().forEach(
               (column) -> System.out.println("update--before--By--Annotation: " + column.getName() + " ::   " + column.getValue())
       );
       rowData.getAfterColumnsList().forEach(
               (column) -> System.out.println("update--after--By--Annotation: " + column.getName() + " ::   " + column.getValue())
       );
   }

   /***
    * 删除数据监听
    * @param eventType
    */
   @DeleteListenPoint
   public void onEventDelete(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
       System.out.println("deleteListenPoint...");
       rowData.getBeforeColumnsList().forEach(
               (column) -> System.out.println("update--before--By--Annotation: " + column.getName() + " ::   " + column.getValue())
       );
   }

    /***
     * 自定义数据修改监听
     * @param eventType
     * @param rowData
     */
    @ListenPoint(
            eventType = {CanalEntry.EventType.UPDATE, CanalEntry.EventType.INSERT, CanalEntry.EventType.DELETE}, // 指定监听类型
            schema = "changgou_content", // 指定库名
            table = {"tb_content_category", "tb_content"}, // 指定表明
            destination = "example" // 指定实例地址
    )
    public void onEventCustomUpdate(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
        System.out.println("customListenPoint...");
        rowData.getBeforeColumnsList().forEach(
                (column) -> System.out.println("custom--before--By--Annotation: " + column.getName() + " ::   " + column.getValue())
        );
        rowData.getAfterColumnsList().forEach(
                (column) -> System.out.println("custom--after--By--Annotation: " + column.getName() + " ::   " + column.getValue())
        );
    }
}

(4)启动类创建

在com.changgou中创建启动类,代码如下:

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableCanalClient
public class CanalApplication {

    public static void main(String[] args) {
        SpringApplication.run(CanalApplication.class,args);
    }
}

(5)测试

启动canal微服务,然后修改任意数据库的表数据,canal微服务后台输出如下:

在这里插入图片描述

5.content广告微服务搭建

在changgou-service中搭建changgou-service-content微服务,对应的dao、service、controller、pojo由代码生成器生成。

首先在changgou-service-api中创建changgou-service-content-api,将pojo拷贝到API工程中,如下图:

在这里插入图片描述

(1)pom.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<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">
    <parent>
        <artifactId>changgou-service</artifactId>
        <groupId>com.changgou</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>changgou-service-content</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.changgou</groupId>
            <artifactId>changgou-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.changgou</groupId>
            <artifactId>changgou-service-content-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

(2)application.yml配置

server:
  port: 18084
spring:
  application:
    name: content
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.211.132:3306/changgou_content?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:7001/eureka
  instance:
    prefer-ip-address: true
feign:
  hystrix:
    enabled: true
mybatis:
  configuration:
    map-underscore-to-camel-case: true  #开启驼峰功能

#hystrix 配置
hystrix:
  command:
    default:
      execution:
        timeout:
        #如果enabled设置为false,则请求超时交给ribbon控制
          enabled: true
        isolation:
          strategy: SEMAPHORE

(3)启动类创建

@SpringBootApplication
@EnableEurekaClient
@MapperScan(basePackages = {"com.changgou.content.dao"})
public class ContentApplication {

    public static void main(String[] args) {
        SpringApplication.run(ContentApplication.class);
    }
}

5.1 广告查询

在content微服务中,添加根据分类查询广告。

(1)业务层

修改changgou-service-content的com.changgou.content.service.ContentService接口,添加根据分类ID查询广告数据,代码如下:

/***
 * 根据categoryId查询广告集合
 * @param id
 * @return
 */
List<Content> findByCategory(Long id);

修改changgou-service-content的com.changgou.content.service.impl.ContentServiceImpl接口实现类,添加根据分类ID查询广告数据,代码如下:

/***
 * 根据分类ID查询
 * @param id
 * @return
 */
@Override
public List<Content> findByCategory(Long id) {
    Content content = new Content();
    content.setCategoryId(id);
    content.setStatus("1");
    return contentMapper.select(content);
}

(2)控制层

修改changgou-service-content的com.changgou.content.controller.ContentController,添加根据分类ID查询广告数据,代码如下:

/***
 * 根据categoryId查询广告集合
 */
@GetMapping(value = "/list/category/{id}")
public Result<List<Content>> findByCategory(@PathVariable Long id){
    //根据分类ID查询广告集合
    List<Content> contents = contentService.findByCategory(id);
    return new Result<List<Content>>(true,StatusCode.OK,"查询成功!",contents);
}

(3)feign配置

在changgou-service-content-api工程中添加feign,代码如下:

@FeignClient(name="content")
@RequestMapping(value = "/content")
public interface ContentFeign {

    /***
     * 根据分类ID查询所有广告
     */
    @GetMapping(value = "/list/category/{id}")
    Result<List<Content>> findByCategory(@PathVariable Long id);
}

5.2 同步实现

在canal微服务中修改如下:

(1)配置redis

修改application.yml配置文件,添加redis配置,如下代码:

在这里插入图片描述

(2)启动类中开启feign

修改CanalApplication,添加@EnableFeignClients注解,代码如下:

在这里插入图片描述

(3)同步实现

修改监听类CanalDataEventListener,实现监听广告的增删改,并根据增删改的数据使用feign查询对应分类的所有广告,将广告存入到Redis中,代码如下:

上图代码如下:

@CanalEventListener
public class CanalDataEventListener {
    @Autowired
    private ContentFeign contentFeign;
    //字符串
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //自定义数据库的 操作来监听
    //destination = "example"
    @ListenPoint(destination = "example",
            schema = "changgou_content",
            table = {"tb_content", "tb_content_category"},
            eventType = {
                    CanalEntry.EventType.UPDATE,
                    CanalEntry.EventType.DELETE,
                    CanalEntry.EventType.INSERT})
    public void onEventCustomUpdate(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
        //1.获取列名 为category_id的值
        String categoryId = getColumnValue(eventType, rowData);
        //2.调用feign 获取该分类下的所有的广告集合
        Result<List<Content>> categoryresut = contentFeign.findByCategory(Long.valueOf(categoryId));
        List<Content> data = categoryresut.getData();
        //3.使用redisTemplate存储到redis中
        stringRedisTemplate.boundValueOps("content_" + categoryId).set(JSON.toJSONString(data));
    }

    private String getColumnValue(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
        String categoryId = "";
        //判断 如果是删除  则获取beforlist
        if (eventType == CanalEntry.EventType.DELETE) {
            for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
                if (column.getName().equalsIgnoreCase("category_id")) {
                    categoryId = column.getValue();
                    return categoryId;
                }
            }
        } else {
            //判断 如果是添加 或者是更新 获取afterlist
            for (CanalEntry.Column column : rowData.getAfterColumnsList()) {
                if (column.getName().equalsIgnoreCase("category_id")) {
                    categoryId = column.getValue();
                    return categoryId;
                }
            }
        }
        return categoryId;
    }
}

测试:

修改数据库数据,可以看到Redis中的缓存跟着一起变化

在这里插入图片描述

6.项目源码→在日志的day04记录

https://gitee.com/bx2star/bd2star-changgou.git

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在Docker中安装Canal,可以按照以下步骤进行操作: 1. 首先,确保已经安装了Docker和Docker Compose。如果没有,请先安装它们。 2. 创建一个新的文件夹,用于存放Canal的配置文件和数据。例如,可以在命令行中运行以下命令来创建一个名为`canal`的文件夹: ``` mkdir canal ``` 3. 在`canal`文件夹中创建一个名为`docker-compose.yml`的文件,并将以下内容复制粘贴到该文件中: ```yaml version: '3' services: canal-server: image: canal/canal-server:v1.1.5 ports: - 11111:11111 volumes: - ./config:/home/admin/canal-server/conf - ./logs:/home/admin/canal-server/logs - ./data:/home/admin/canal-server/data ``` 4. 在`canal`文件夹中创建一个名为`instance.properties`的文件,并将以下内容复制粘贴到该文件中: ``` canal.instance.master.address=127.0.0.1:3306 canal.instance.dbUsername=your_username canal.instance.dbPassword=your_password canal.instance.connectionCharset=UTF-8 ``` 将`your_username`和`your_password`替换为实际的MySQL数据库用户名和密码。 5. 在命令行中切换到`canal`文件夹,并运行以下命令启动Canal服务: ``` docker-compose up -d ``` 这将启动Canal服务,并将其绑定到主机的`11111`端口。 现在,Canal已经在Docker中安装并运行了。你可以通过访问`127.0.0.1:11111`来验证它是否正常工作。请注意,你需要在`instance.properties`文件中配置正确的MySQL数据库连接信息才能使Canal与数据库进行通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

iBaoxing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值