Canal初体验:Springboot整合Canal实现缓存数据一致demo,遇到的问题及解决方案

文章目录

  • SpringBoot整合Canal实现缓存更新功能
    • 1、前提条件
      • 1.1 [canal详细安装教程](http://t.csdn.cn/W6QJO)
      • 1.2 理论依据
    • 2、导入相关依赖并配置
    • 3、demo示例代码
    • 4、遇到的问题
  • 总结


SpringBoot整合Canal实现缓存更新功能

1、前提条件

1.1 canal详细安装教程

1.2 理论依据

Canal是基于mysql的主从同步实现,简单的说就是Canal假装成mysql的一个子节点,开启一个线程不断的读取mysql的binlog日志(此日志记录的是mysql的执行命令),将binlog日志的内容拷贝到中继日志(relay log),然后再开启一个线程去不断重放中继日志中的执行命令,以此达到数据一致。放在代码中就是,只要继承了EntryHandler并指定数据库表,那么对应表添加或者修改一行数据的时候会在binlog上生成一条日志,canal读取到这条日志之后会产生一条消息,并封装成EntryHandler中指定的实体类,这时候再针对这条实体类数据进行处理。


2、导入相关依赖并配置

默认数据库、redis的基础环境已搭建好,持久层使用的是mybatisPlus

<dependency>
    <groupId>top.javatoolgroupId>
    <artifactId>canal-spring-boot-starterartifactId>
    <version>1.2.1-RELEASEversion>
dependency>
canal:
	# 此处的destination是创建canal时指定的,也可以在canal.properties文件中调整
    destination: test
    server: 192.168.56.10:11111

3、demo示例代码

①主要业务实现:消息拦截器

import top.javatool.canal.client.annotation.CanalTable;
import top.javatool.canal.client.handler.EntryHandler;

/**
 * @Author Jzs
 * @Date 2023/3/9 21:08
 */
@CanalTable("tb_customer")
@Component
public class CustomerHandler implements EntryHandler<Customer>, InitializingBean {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CustomerService customerService;

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private static final String CUSTOMER_KEY = "customer:";


    @Override
    public void afterPropertiesSet() throws Exception {
        //提前加载数据库中的数据到redis中
        customerService.list().forEach(this::saveCustomer);
    }

    @Override
    public void insert(Customer customer) {
        saveCustomer(customer);
    }

    @Override
    public void update(Customer before, Customer after) {
        saveCustomer(after);
    }

    @Override
    public void delete(Customer customer) {
        //删除对应的redis缓存
        stringRedisTemplate.delete(CUSTOMER_KEY + customer.getId());
    }

    private void saveCustomer(Customer customer){
        try {
            //写数据到redis缓存
            String json = MAPPER.writeValueAsString(customer);
            stringRedisTemplate.opsForValue().set(CUSTOMER_KEY + customer.getId(),json);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

②实体类

@Data
@TableName("tb_customer")
public class Customer {

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 手机号码
     */
    private String phone;

    /**
     * 密码,加密存储
     */
    private String password;

    /**
     * 昵称,默认是随机字符
     */
    private String nickName;

    /**
     * 用户头像
     */
    private String icon = "";

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}

③业务层略
④controller层

@CrossOrigin
@RestController
@RequestMapping("/customer")
public class CustomerController {

    @Resource
    private CustomerService service;

    @PostMapping("/save")
    public void save(@RequestBody Customer customer){
        service.save(customer);
    }

    @PutMapping("/update")
    public void update(@RequestBody Customer customer){
        service.updateById(customer);
    }

    @DeleteMapping("/delete/{id}")
    public void delete(@PathVariable("id") Integer id){
        service.removeById(id);
    }
}

4、遇到的问题

以插入数据为例,整个canal的调用链上使用到的类大概有以下流程,最后处理完消息的数据之后才会调用步骤2图片中的entryHandler.insert(object),也就是CustomerHandler 中的insert方法
Canal初体验:Springboot整合Canal实现缓存数据一致demo,遇到的问题及解决方案_第1张图片

①部分字段的数据丢失
获取数据是通过反射获取到类中所有字段作为key,由于类中部分字段跟数据库的字段对不上导致获取不到数据,可以看到步骤4中,可以通过Column注解进行指定。
②添加好字段之后,数据转化出现问题,这个主要是由于步骤6中,字符串转对应类型的时候StringConvertUtil的convertType方法中并没有这个LocalDateTime的判断转化,此处比较简单的一个修改方案是直接将LocalDateTime换成Date
③简单修改后的相关字段如下:

@Column(name = "nick_name")
private String nickName;
@Column(name = "create_time")
private Date createTime;
@Column(name = "update_time")
private Date updateTime;

③比较建议直接下载canal-spring-boot-starter的源码,进行对应修改之后再打包成对应的依赖进行导入,这样子相对来说比较可控,也可以定制化处理一些特殊的问题,修改的话可以大概参照图片的流程。


总结

其实canal的使用并不复杂,上面只是一个简单的例子,部分方法可以单独抽出来放在各自的工具类中,出于方便只展示了redis一种方式,其实还可以替换成其他类型的缓存中间件进行使用。

你可能感兴趣的:(spring系列,spring,boot,java,数据库,redis,缓存)